Python装饰器
Posted tsuko
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Python装饰器相关的知识,希望对你有一定的参考价值。
Python装饰器
一个生动的比喻是,被装饰函数是画作,而装饰器就是外面的画框。装饰器为被装饰函数提供额外的功能和行为;可以提取多个函数的共同部分作为装饰器,从而使代码更加清晰。
学习装饰器要逐步解决的问题:
- 如何定义,使用装饰器?装饰器的执行顺序如何?
- 装饰器的接口是怎么样的?被装饰函数及其参数是如何传递的?
- 装饰器的高级用法:参数化装饰器和类装饰器
装饰器的基本知识
装饰器的参数是被修饰的函数,在执行完额外工作后,要返回被装饰函数或另一个函数。
# 装饰器定义 def d1(func): print("decorator_1") return func # 放置装饰器 @d1 def func(n) print(n)
上面的代码中,执行func()这个函数等价于 func = d1( func),也就是说,执行代码时,func是被d1装饰的结果的引用,不是原始的func。f = func(100),print(f)和print(func)结果不一样。
装饰器另外一个关键特性是:在加载模块时,他们在被装饰的函数定义之后立即执行,可见下面的代码:
def decorator_1(func): def miao(func): print("fck you, return func") return func print("decorator_1, return miao") return miao def decorator_2(func): print("decorator_2, return func") return func #叠放装饰器,结果等价于d1( d2( func_1)) @decorator_1 @decorator_2 def func_1(val): print(val) if __name__ == "__main__": print("Main()") func_1(1000)
执行结果为:
decorator_2, return func decorator_1, return miao Main() fuck you, return func
这说明d2,d1两个装饰器在真正的被装饰函数func调用前,被执行。假如d2装饰了n个函数,那么d2要先执行n次。
参数传递
通常,装饰器内部定义了函数,这个内部函数执行额外的工作。这种结构涉及“闭包”的概念。所谓闭包,就是那些可以使用非全局变量、非自身的变量的函数,例如一个函数1内部定义了函数2,函数2可以使用函数1的变量,那么函数2就是一个闭包。
# 《Fluent Python》page 165 example-7-15 import time def clock(func): def clocked(*args): # ?? # extra .. result = func(*args) return result return clocked
那么这里就有问题了:
Q1:*args是谁的参数?
从上下文中可知*args应该是func接受的函数,但是在上文装饰器的定义中,没有显式看到*args。由此推测:装饰器中的*args, **kwargs这些容器分别存放了被修饰函数func的positional args 和 keyword args。
Q2:上面的假设基于di(func)的,如果改为d1(func, *args, **kwargs),那么这两个容器放的是谁的参数?
ans:经过尝试,发现会抛出异常。如果写:
- @d1(**dict1), d1()missing a required positional arg ‘func‘.
- @d1(kwargs=dict1) , d1()missing a required positional arg ‘func‘.
- @d1(func=func_1, kwargs=dict1), name ‘func_1‘ is not defined.
通过查看他人代码实例,发现每个装饰器及其内部的函数,都有一个函数只有func一个参数。在《Fluent Python》page 76 example-7-25的注释中,第二条注释写道:decorate(func)是真正的函数。第一条注释:clock(...)是装饰器工厂函数。所以在Q2中,d1这个入口不是真正的装饰器,装饰器的参数只有一个func。
下面的代码很有趣:
dict1 = { "name":"miao", "type":"zhizhi" } dict2 = { "name":"guagua", } def decorator_1(**kwargs): def miao(func): def zhi(**kwargs): for k,v in kwargs.items(): # 打印dict1 print(k, v) return func return zhi for k,v in kwargs.items(): # 打印dict2 print(k, v) print("decorator_1, return miao") return miao #d1( d2(func_1(1000))) @decorator_1(**dict2) def func_1(**kwargs): print("fuck") if __name__ == "__main__": func_1(**dict1)
d1接受的是dict2, func_1的参数是dict1, 最终逐个打印出来的是dict1的key-value,也就是说:miao(func)是真正的装饰器,其内部函数使用的**kwargs, *args都是被装饰函数的参数,作为工厂函数的d1,它接受的参数是外部直接传给的,区别于func的参数。
参数化装饰器
以上是关于Python装饰器的主要内容,如果未能解决你的问题,请参考以下文章