简介
装饰器是可调用的对象, 其参数是另一个函数 (被装饰的函数). python 的世界中, 一切皆对象, 把函数看做一个对象, 一个可以拼接, 编辑的对象. 执行对象的方法是__ 对象 ()__
装饰器只是 python 的语法糖
观察下面的两个调用, 他们的效果是一样的.
- #!/usr/bin/env python
- #-*-coding:utf-8-*-
- '''
- @file: dec4.py
- @time: 2018/12/13 17:15
- '''
- import functools
- def register(func):
- @functools.wraps(func)
- def warpper(*args,**kwargs):
- print("啦啦啦, 这里装饰函数 %s"%func.__name__)
- result = func() #这里就是真正的执行逻辑
- return result
- return warpper # 返回包装后的函数
- @register
- def f1():
- print("使用 @语法的装饰器 %s"%f1.__name__)
- def f2():
- print("没 @语法的装饰器 %s" % f2.__name__)
- if __name__ == '__main__':
- f1()
- register(f2)()
- #或者
- # f_ = register(f2)
- # f_()
装饰器启动装饰的时机
1, 文件导入时装饰器已经启动对函数的装饰.
装饰器的一个关键特性是, 它们在被装饰的函数定义之后立即运行. 这通常是在导入时 (即 Python 加载模块时).--cookbook
创建文件 dec1.py
- # dec1.py
- registry = [] # 存放注册函数
- def register(func):
- print('装饰器入列表 (%s)' % func.__name__)
- registry.append(func)
- return func
- @register
- def f1():
- print('执行 %s'%f1.__name__)
- @register
- def f2():
- print('执行 %s' % f2.__name__)
创建文件 dec2.py
# dec2.py
from 你的目录 import dec1
执行文件 dec2.py 输出如下:
装饰器返回的结果
1, 返回目标函数. 2, 返回目标函数的结果.
以上 register 的用法适用于对函数进行预处理的场景, 例如 flask 框架的路由注册, 在 flask App 启动时将所有的函数收集起来, 与注册的路径进行一一对应, 但是函数运行时跟原函数没有任何区别, 因为装饰器返回的还是目标函数. 假如我们需要在函数运行时进行日志的输出, 则需要将函数进行一层封装, 即将目标函数包装成另一个函数, 这时返回的是另一个函数, 只是包装后的函数可以进行额外的操作.
以下是两个装饰器的写法, 注意区别.
- import functools
- def register1(func): #在文件引入时返回目标函数的装饰器
- #这里进行一些函数预处理的操作, 例如将函数名字收集进一个列表
- #list.append(func.__name__)
- return func
- def register2(func): #在函数运行时返回函数的装饰器
- @functools.wraps(func)
- def warpper(*args,**kwargs):
- print("函数没有执行 %s"%func.__name__)
- result = func #这里就是真正的执行逻辑 #注意 ----- 这里没有执行函数
- return result
- return warpper # 返回包装后的函数
- def register3(func): #返回目标函数结果的装饰器
- @functools.wraps(func)
- def warpper(*args,**kwargs):
- print("函数开始执行 %s"%func.__name__)
- result = func() #这里就是真正的执行逻辑
- return result
- return warpper # 返回包装后的函数
- @register1
- def f1():
- print("执行函数 %s"%f1.__name__)
- @register2
- def f2():
- print("执行函数 %s"%f2.__name__)
- @register3
- def f3():
- print("执行函数 %s"%f3.__name__)
- if __name__ == '__main__':
- f1()
- f2()
- f3()
执行结果:
这时添加代码:
- if __name__ == '__main__':
- f1()
- f2()
- f_ = f2()
- print("开始真正执行 f2 函数啦")
- f_()
- f3()
执行结果:
装饰函数的属性保持
1, 使用 @functools.wraps
你会发现下面的代码中, 如果 wrapper 函数没有使用__@functools.wraps 装饰__的话, f1.__name__返回的是 wrapper 函数的名字, 这是因为我们返回的函数已经是 wrapper 这个函数, 所以__name__属性也就不是原来函数的了,__@functools.wraps 装饰器__的作用就是将目标函数的属性例如__name__等原封不动转移给包装好的函数.
- #!/usr/bin/env python
- #-*-coding:utf-8-*-
- '''
- @file: dec4.py
- @time: 2018/12/13 17:15
- '''
- import functools
- def register(func): #返回目标函数结果的装饰器
- def warpper(*args,**kwargs):
- result = func() #这里就是真正的执行逻辑
- return result
- return warpper # 返回包装后的函数
- def register2(func): #返回目标函数结果的装饰器
- @functools.wraps(func)
- def warpper(*args,**kwargs):
- result = func() #这里就是真正的执行逻辑
- return result
- return warpper # 返回包装后的函数
- @register
- def f1():
- print("函数名为 %s"%f1.__name__)
- @register2
- def f2():
- print("函数名为 %s" % f2.__name__)
- if __name__ == '__main__':
- f1()
- f2()
- #或者
- #f_ = register(f2)
- #f_()
带参数的装饰器
1, 外层嵌套一层函数传参, 返回一个装饰器.
装饰器也是一个对象, 只要使用一个函数封装装饰器, 返回目标装饰器即可, 这时就可以通过外层的函数传进参数.
- #!/usr/bin/env python
- # -*-coding:utf-8-*-
- '''
- @author: 黄伟文
- @email: huangweiwen@wtoip.com
- @file: dec5.py
- @time: 2018/12/13 18:13
- '''
- import functools
- def dec_arg(arg=None):
- def register(func):
- @functools.wraps(func)
- def wrapper(*args, **kwargs):
- print('传进的参数为 %s' % arg)
- return func(*args, **kwargs)
- return wrapper
- return register #这是装饰器
- @dec_arg(arg='aaaa')
- def f1():
- print("执行 %s"%f1.__name__)
- def f2():
- print("执行 %s" % f2.__name__)
- if __name__ == '__main__':
- f1()
- f2_ = dec_arg(arg='bbbb')(f2)
- f2_()
- dec_arg(arg='cccc')(f2)()
来源: https://juejin.im/post/5c123309f265da617974b32d