简介

装饰器是可调用的对象,其参数是另一个函数(被装饰的函数)。
python的世界中,一切皆对象,把函数看做一个对象,一个可以拼接、编辑的对象。执行对象的方法是 对象()

装饰器只是python的语法糖

观察下面的两个调用,他们的效果是一样的。

  1. #!/usr/bin/env python
  2. #-*-coding:utf-8-*-
  3. '''
  4. @file: dec4.py
  5. @time: 2018/12/13 17:15
  6. '''
  7. import functools
  8. def register(func):
  9. @functools.wraps(func)
  10. def warpper(*args,**kwargs):
  11. print("啦啦啦,这里装饰函数 %s"%func.__name__)
  12. result = func() #这里就是真正的执行逻辑
  13. return result
  14. return warpper # 返回包装后的函数
  15. @register
  16. def f1():
  17. print("使用@语法的装饰器 %s"%f1.__name__)
  18. def f2():
  19. print("没@语法的装饰器 %s" % f2.__name__)
  20. if __name__ == '__main__':
  21. f1()
  22. register(f2)()
  23. #或者
  24. # f_ = register(f2)
  25. # f_()

python | 装饰器入门探究 - 图1

装饰器启动装饰的时机

1、文件导入时装饰器已经启动对函数的装饰。

装饰器的一个关键特性是,它们在被装饰的函数定义之后立即运行。
这通常是在导入时(即 Python 加载模块时)。—cookbook

创建文件 dec1.py

  1. # dec1.py
  2. registry = [] # 存放注册函数
  3. def register(func):
  4. print('装饰器入列表(%s)' % func.__name__)
  5. registry.append(func)
  6. return func
  7. @register
  8. def f1():
  9. print('执行 %s'%f1.__name__)
  10. @register
  11. def f2():
  12. print('执行 %s' % f2.__name__)

创建文件 dec2.py

  1. # dec2.py
  2. from 你的目录 import dec1

执行文件 dec2.py
输出如下:
python | 装饰器入门探究 - 图2

装饰器返回的结果

1、返回目标函数。
2、返回目标函数的结果。
以上 register的用法适用于对函数进行预处理的场景,例如flask框架的路由注册,在flask app启动时将所有的函数收集起来,与注册的路径进行一一对应,但是函数运行时跟原函数没有任何区别,因为装饰器返回的还是目标函数。假如我们需要在函数运行时进行日志的输出,则需要将函数进行一层封装,即将目标函数包装成另一个函数,这时返回的是另一个函数,只是包装后的函数可以进行额外的操作。
以下是两个装饰器的写法,注意区别。

  1. import functools
  2. def register1(func): #在文件引入时返回目标函数的装饰器
  3. #这里进行一些函数预处理的操作,例如将函数名字收集进一个列表
  4. #list.append(func.__name__)
  5. return func
  6. def register2(func): #在函数运行时返回函数的装饰器
  7. @functools.wraps(func)
  8. def warpper(*args,**kwargs):
  9. print("函数没有执行 %s"%func.__name__)
  10. result = func #这里就是真正的执行逻辑 #注意-----这里没有执行函数
  11. return result
  12. return warpper # 返回包装后的函数
  13. def register3(func): #返回目标函数结果的装饰器
  14. @functools.wraps(func)
  15. def warpper(*args,**kwargs):
  16. print("函数开始执行 %s"%func.__name__)
  17. result = func() #这里就是真正的执行逻辑
  18. return result
  19. return warpper # 返回包装后的函数
  20. @register1
  21. def f1():
  22. print("执行函数 %s"%f1.__name__)
  23. @register2
  24. def f2():
  25. print("执行函数 %s"%f2.__name__)
  26. @register3
  27. def f3():
  28. print("执行函数 %s"%f3.__name__)
  29. if __name__ == '__main__':
  30. f1()
  31. f2()
  32. f3()

执行结果:
python | 装饰器入门探究 - 图3
这时添加代码:

  1. if __name__ == '__main__':
  2. f1()
  3. f2()
  4. f_ = f2()
  5. print("开始真正执行f2函数啦")
  6. f_()
  7. f3()

执行结果:
python | 装饰器入门探究 - 图4

装饰函数的属性保持

1、使用@functools.wraps

你会发现下面的代码中,如果wrapper函数没有使用@functools.wraps装饰的话,f1.name返回的是wrapper函数的名字,这是因为我们返回的函数已经是wrapper这个函数,所以name属性也就不是原来函数的了,@functools.wraps装饰器的作用就是将目标函数的属性例如name等原封不动转移给包装好的函数。

  1. #!/usr/bin/env python
  2. #-*-coding:utf-8-*-
  3. '''
  4. @file: dec4.py
  5. @time: 2018/12/13 17:15
  6. '''
  7. import functools
  8. def register(func): #返回目标函数结果的装饰器
  9. def warpper(*args,**kwargs):
  10. result = func() #这里就是真正的执行逻辑
  11. return result
  12. return warpper # 返回包装后的函数
  13. def register2(func): #返回目标函数结果的装饰器
  14. @functools.wraps(func)
  15. def warpper(*args,**kwargs):
  16. result = func() #这里就是真正的执行逻辑
  17. return result
  18. return warpper # 返回包装后的函数
  19. @register
  20. def f1():
  21. print("函数名为 %s"%f1.__name__)
  22. @register2
  23. def f2():
  24. print("函数名为 %s" % f2.__name__)
  25. if __name__ == '__main__':
  26. f1()
  27. f2()
  28. #或者
  29. #f_ = register(f2)
  30. #f_()

执行结果:
python | 装饰器入门探究 - 图5

带参数的装饰器

1、外层嵌套一层函数传参,返回一个装饰器。

装饰器也是一个对象,只要使用一个函数封装装饰器,返回目标装饰器即可,这时就可以通过外层的函数传进参数。

  1. #!/usr/bin/env python
  2. # -*-coding:utf-8-*-
  3. '''
  4. @author: 黄伟文
  5. @email: huangweiwen@wtoip.com
  6. @file: dec5.py
  7. @time: 2018/12/13 18:13
  8. '''
  9. import functools
  10. def dec_arg(arg=None):
  11. def register(func):
  12. @functools.wraps(func)
  13. def wrapper(*args, **kwargs):
  14. print('传进的参数为 %s' % arg)
  15. return func(*args, **kwargs)
  16. return wrapper
  17. return register #这是装饰器
  18. @dec_arg(arg='aaaa')
  19. def f1():
  20. print("执行 %s"%f1.__name__)
  21. def f2():
  22. print("执行 %s" % f2.__name__)
  23. if __name__ == '__main__':
  24. f1()
  25. f2_ = dec_arg(arg='bbbb')(f2)
  26. f2_()
  27. dec_arg(arg='cccc')(f2)()

python | 装饰器入门探究 - 图6