本文将从以下几点讨论python3中的装饰器:
闭包传递函数引用
什么是装饰器
@注解完成装饰器
装饰器的装饰time
装饰带参数的函数
装饰带返回值的函数
装饰器带参数
多个装饰器
保留被装饰函数的__name__属性
在python中函数,对象统统都可以赋值给变量,如果把一个函数赋值给一个变量,那么通过这个变量就可以调用函数。有关闭包可查阅小编涂鸦之作—【python3闭包】。这里我们将函数直接传到闭包里完成调用。
(func): (): func()(): ()
什么是装饰器
通常有些函数功能是非业务性质的(权限、日志、事务等),我们希望将这些函数功能植入到业务函数处理中,又不想破坏业务函数的定义,这种在运行期动态增强功能的方式就是装饰器。上面的示例中,显然已经拿到了目标函数的引用,我们就可以在目标函数执行前、执行后进行增强。这里我们在函数执行前后打印日志。
(func): (): () func() ()
注解完成装饰器
结合示例1和示例2我们就完成了装饰器的使用,没有改变目标函数定义,增强了目标函数功能。但是在调用上略显吃力,我们可以直接使用@+装饰器函数置于目标函数上,完成装饰操作。
(func): (): () func() ()
装饰器的装饰time
python3是解释型编程语言,从上至下依次解释执行。所以在目标函数执行前装饰器已经装饰好了。下面我们并没有调用任何函数,执行时装饰器已生效。
() func() ()
装饰带参数的函数
很多时候目标函数都是带参数的,这里使用标准的不定长传参,注意在调用目标函数时,元组参数args要加*,关键字参数kwargs加**,用于拆包,否则目标函数接收到的是一个元组和字典。函数定义时,*args和**kwargs用来声明将元组参数接收到args中,将关键字参数接收到kwargs中。
time(func): () (*args**kwargs): start = time.time() (% start) func(*args**kwargs) end = time.time() (% (end(end - start))) wrap(name*args**kwargs): time.sleep() (nameargskwargs)__name__ == :
装饰带返回值的函数
目标函数有返回值的情况很容易处理,经过上面的示例,我们知道装饰器就是将目标函数作为变量传入,然后由装饰器触发目标函数。带有返回值的函数,只需由装饰器内部接收返回值,并返回即可。
(func): (*args**kwargs): func(*args**kwargs) wrap():__name__ == :
装饰器带参数
很多时候装饰器自身也需要参数支持,比如记录日志我们希望知道是哪个业务模块的,或日志的级别等。上面的示例装饰器用来接收函数变量名了,装饰器内部的嵌套函数用来接收目标函数参数了,所以装饰器想要接收参数只能再来一层嵌套函数了。
(name): (func): (*args
(% name) func(*args
(% name) logging wrap()(num):
多个装饰器
有时需要将多个装饰器作用于目标函数,使用上和单个装饰器并无区别。下面我们使用两个装饰器作为演示,其实装饰器本质就是传递函数引用,且只能作用于函数。第一个装饰器找不到函数就会向下继续执行,第二个装饰器找到目标函数进行装饰然后返回内部函数,第一个装饰器就以第二个装饰器返回的函数作为目标函数,所以装饰器最后的执行顺序就是装饰器声明的顺序,而装饰的顺序是由近到远(远近是相对于目标函数而言)。
time(func): () (): start = time.time() (% start) func() end = time.time() (% (end(end - start))) wrap(func): () (): () func() () wrap(): ()__name__ == :
保留被装饰函数的__name__属性
目标函数被装饰器装饰后,由于目标函数是作为参数传递到装饰器函数中,而装饰器函数内部是返回嵌套函数的引用,所以查看目标函数的__name__属性时,发现属性名称为装饰器内部嵌套函数名称。我们并不需要对__name__属性进行赋值操作,python内置functools.wraps可以解决这个问题。
functools(func): (): func() wrap(func): (func) (): func() wrap(): ()(): ()__name__ == :(echo.)