装饰器概念

装饰器就是修改增加其他函数功能的特殊函数,有助于让代码更简短。

装饰器都是高阶函数,但装饰器是对传入函数功能的装饰和增强

无参装饰器

  • 是一个函数
  • 函数作为它
  • 的形参
  • 返回值也是一个函数
  • 使用@functionname方式,@下的函数名称会作为装饰器的参数传给装饰器。

示例

对加法函数进行功能增强

  1. def logger(fn):
  2. def wapper(*args, **kwargs):
  3. print('begin')
  4. x = fn(*args, **kwargs)
  5. print('end')
  6. return x
  7. return wapper
  8. @logger # 等价于add = logger(add)
  9. def add(x, y):
  10. return x + y
  11. print(add(40, 50))
  12. print(add.__name__)
  13. # output
  14. begin
  15. end
  16. 90
  17. wapper

这里会发现一个问题,add.name应该输出的是add,这里被装饰器wapper替代了,这是因为装饰器会重写我们函数的名字和注释文档,Python提供了一个函数来解决这个问题,functools.wraps,修改如下

  1. from functools import wraps
  2. def logger(fn):
  3. @wraps(fn)
  4. def wapper(*args, **kwargs):
  5. print('begin')
  6. x = fn(*args, **kwargs)
  7. print('end')
  8. return x
  9. return wapper
  10. @logger
  11. def add(x, y):
  12. return x + y
  13. print(add(40, 50))
  14. print(add.__name__)
  15. # output
  16. begin
  17. end
  18. 90
  19. add

带参装饰器

  • 带参装饰器也是一个函数
  • 函数作为它的形参
  • 返回值是一个不带参的装饰器函数
  • 使用@functionname(参数列表)方式调用
  • 可以看做在装饰器外层又增加了一层函数

示例
  1. from functools import wraps
  2. def logger(logfile='test.log'): # 带参装饰器
  3. def _logging(fn): # 将被装饰的函数作为参数传入
  4. @wraps(fn) # 等价于wraps= wraps(fn)(wapper)
  5. def wapper(*args, **kwargs):
  6. log_string = fn.__name__ + ' was called'
  7. print(log_string)
  8. with open(logfile, 'a') as f:
  9. f.write(log_string + '\n')
  10. return fn(*args, **kwargs) # 装饰后将原函数返回
  11. return wapper
  12. return _logging
  13. @logger(logfile='out.log')
  14. def add(x, y):
  15. return x+y
  16. print(add(5,6))
  17. # 这里会出现一个叫out.log的文件,里面的内容就是装饰器装饰过的字符串

类装饰器

除了上面的装饰器函数,类也能构建装饰器。

下面使用类来重新构建logger

  1. from functools import wraps
  2. class logger():
  3. def __init__(self, logfile='test.log'):
  4. self.logfile = logfile
  5. def __call__(self, fn): # 类装饰器主要依赖call方法来实现
  6. @wraps(fn)
  7. def wapper(*args, **kwargs):
  8. log_string = fn.__name__ + ' was called'
  9. print(log_string)
  10. with open(self.logfile, 'a') as f:
  11. f.write(log_string + ' 类装饰器')
  12. return fn(*args, **kwargs)
  13. return wapper
  14. @logger(logfile='out.log')
  15. def add(x, y):
  16. return x+y
  17. print(add(5,11))
  • 相比函数装饰器,类装饰器灵活度更好,使用类装饰器主要依靠类的call方法,使用方法同样使用@加装饰器名称方法