基础知识
装饰器是可调用的对象,其参数是另一个函数(被装饰的函数)。装饰器可能会处理被装饰的函数,然后把它返回,或者将其替换成另一个函数或可调用对象。
特性:
能把被装饰的函数替换成其他函数
def wrapper(func): def inner(*args, **kwargs): print("hello, i am inner") ret = func(*args, **kwargs) return ret return inner @wrapper def fun(): print("hello world, i am fun")
运行结果:
>>> fun() hello, i am inner hello world, i am fun >>> fun <function wrapper.<locals>.inner at 0x000001467E56AAF8>
结论:调用被装饰的
fun
其实会运行inner
, 由最后结果可以发现fun
现在是inner
的引用
在被装饰的函数定义之后立即运行。这通常是在导入时(即
Python
加载模块时)创建
test_wrap.py
文件,并写入:def test_wrap(func): print("it is run test_wrap") def wrap(*args, **kwargs): print("it is run wrap") return func(*args, **kwargs) return wrap @test_wrap def test(): print('run complete test()')
if __name__ == '__main__':
print('running test()')
test()
```
运行脚本并返回结果:`python test_wrap.py`
```shell
it is run test_wrap
running test()
it is run wrap
run complete test()
```
结论:函数装饰器在导入模块时立即执行,而被装饰的函数只在明确调用时运行。
参数化装饰器
创建一个装饰器工厂函数,把参数传给它,返回一个装饰器,然后再把它应用到要装饰的函数上。
def test_params_wrap(v):
def test_wrap(func):
def wrap(*args, **kwargs):
print(v)
return func(*args, **kwargs)
return wrap
return test_wrap
@test_params_wrap("hello test_params")
def run():
print("run")
if __name__ == '__main__':
run()
运行结果:
hello test_params
run
多个装饰器
看个示例:
def d1(func):
def inner():
print("执行了d1 func")
return func()
return inner
def d2(func):
def inner():
print("执行了 d2 func")
return func()
return inner
@d1
@d2
def f():
print("f")
if __name__ == '__main__':
f()
# result:
# 执行了d1 func
# 执行了 d2 func
# f
结论: 把 @d1
和 @d2
两个装饰器按顺序应用到 f
函数上,作用相当于 f =d1(d2(f))
。换句话说:多个装饰器装饰同一个函数时,执行顺序是从上到下的。
@wraps
的作用
@wraps(func)
的作用: 不改变使用装饰器原有函数的结构(如name, doc)。
不加
@wraps(func)
的装饰器def d1(func): def inner(): """ 我是inner 函数 """ print("执行了d1 func") return func() return inner
@d1
def f():
""" 我是f函数 """
print("f")
if __name__ == '__main__':
f()
print("f name: ", f.__name__, ",f doc: ", f.__doc__)
# result:
# 执行了d1 func
# f
# f name: inner ,f doc: 我是inner 函数
```
结论: 返回的是装饰器的名字和注释,虽不影响程序的运行,但后面查问题或者做其它总归不好。
加
@wraps(func)
的装饰器from functools import wraps def d1(func): @wraps(func) def inner(): """ 我是inner 函数 """ print("执行了d1 func") return func() return inner @d1 def f(): """ 我是f函数 """ print("f")
if __name__ == '__main__':
f()
print("f name: ", f.__name__, ",f doc: ", f.__doc__)
# result:
# 执行了d1 func
# f
# f name: f ,f doc: 我是f函数
```
结论:不改变原函数的名字和注释等
参考书籍: Fluent Python