python 装饰器
young / / / 阅读量

简介

装饰器实际上就是为了给某程序增添功能,但该程序已经上线或已经被使用,那么就不能大批量的修改源代码,这样是不科学的也是不现实的,因为就产生了装饰器,使得其满足:

不能修改被装饰的函数的源代码
不能修改被装饰的函数的调用方式
满足1、2的情况下给程序增添功能
那么根据需求,同时满足了这三点原则,这才是我们的目的。因为,下面我们从解决这三点原则入手来理解装饰器。

装饰器的原则组成:
< 函数+实参高阶函数+返回值高阶函数+嵌套函数+语法糖 = 装饰器 >
这个式子是贯穿装饰器的灵魂所在!

基本的装饰器

一般而言,我们要想拓展原来函数代码,最直接的办法就是侵入代码里面修改,例如:

import time
def func():
    print("hello")
    time.sleep(1)
    print("world")

这是我们最原始的的一个函数,然后我们试图记录下这个函数执行的总时间,

我们先实现一个最简陋的装饰器,不使用任何语法糖和高级语法

improt time

def timer(func) #5
    def deco():  
        start = time.time()
        func()
        stop = time.time()
        print(stop-start)
    return deco

test = timer(test) #6

def test():
    time.sleep(2)
    print("test is running!")   
test() #7

首先,在#6处,把test作为参数传递给了timer(),此时,在timer()内部,func = test,接下来,定义了一个deco()函数,当并未调用,只是在内存中保存了,并且标签为deco。在timer()函数的最后返回deco()的地址deco。

然后再把deco赋值给了test,那么此时test已经不是原来的test了,也就是test原来的那些函数体的标签换掉了,换成了deco。那么在#7处调用的实际上是deco()。

所以这里装饰器就像一个注入符号:有了它,拓展了原来函数的功能既不需要侵入函数内更改代码,也不需要重复执行原函数。

真正的装饰器

根据以上分析,装饰器在装饰时,需要在每个函数前面加上:

test = timer(test)

显然有些麻烦,Python提供了一种语法糖,即:

@timer

这两句是等价的,只要在函数前加上这句,就可以实现装饰作用。

以上为无参形式

装饰有参函数

improt time

def timer(func)
    def deco(*args, **kwargs):  
        start = time.time()
        func(*args, **kwargs)
        stop = time.time()
        print(stop-start)
    return deco

@timer
def test(parameter): #8
    time.sleep(2)
    print("test is running!")   
test() 

那么我们再考虑个问题,如果原函数test()的结果有返回值呢?比如:

def test(parameter): 
    time.sleep(2)
    print("test is running!")   
    return "Returned value"

那么面对这样的函数,如果用上面的代码来装饰,最后一行的test()实际上调用的是deco()。有人可能会问,func()不就是test()么,怎么没返回值呢?

其实是有返回值的,但是返回值返回到deco()的内部,而不是test()即deco()的返回值,那么就需要再返回func()的值,因此就是:

improt time

def timer(func)
    def deco(*args, **kwargs):  
        start = time.time()
        res = func(*args, **kwargs)
        stop = time.time()
        print(stop-start)
        return res 
    return deco

@timer
def test(parameter): #8
    time.sleep(2)
    print("test is running!")   
    return "Returned value"
test() 

带参数的装饰器

又增加了一个需求,一个装饰器,对不同的函数有不同的装饰。那么就需要知道对哪个函数采取哪种装饰。因此,就需要装饰器带一个参数来标记一下。例如:
@decorator(parameter = value)

import time
def timer(parameter):

    def outer_wrapper(func):

        def wrapper(*args, **kwargs):
            if parameter == 'task1':
                start = time.time()
                func(*args, **kwargs)
                stop = time.time()
                print("the task1 run time is :", stop - start)
            elif parameter == 'task2':
                start = time.time()
                func(*args, **kwargs)
                stop = time.time()
                print("the task2 run time is :", stop - start)

        return wrapper

    return outer_wrapper

@timer(parameter='task1')
def task1():
    time.sleep(2)
    print("in the task1")

@timer(parameter='task2')
def task2():
    time.sleep(2)
    print("in the task2")

task1()
task2()
支付宝捐赠
请使用支付宝扫一扫进行捐赠
微信捐赠
请使用微信扫一扫进行赞赏
有 0 篇文章