装饰器的实现

任何可调用对象(任何实现了call方法的对象都是可调用的)都可以用作装饰器,他们返回的对象往往是实现了自己的call方法的更复杂的类的实例

装饰器使用语法

装饰器本质也是一个函数,其返回值就是一个函数对象
使用方法:
1.先定义一个装饰函数(或者类或者偏函数实现)
2.再定义业务函数或者类
3.将业务函数作为参数传入装饰函数

装饰器通用模板

函数装饰器通用模板:

  1. def mydecorator(function):
  2. def wrapped(*args, **kwargs):
  3. # 在调用原始函数之前,做点什么
  4. result = function(*args, **kwargs)
  5. # 在函数调用之后,做点什么
  6. # 并返回结果
  7. return result
  8. # 返回 wrapper 作为装饰函数
  9. return wrapped

类装饰器通用模板:如果装饰器需要复杂的参数化或者依赖于特定状态,使用用户自定义类可能更好

  1. class DecoratorAsClass:
  2. def __init__(self, function):
  3. self.function = function
  4. def __call__(self, *args, **kwargs):
  5. # 调用原始函数前,做点什么
  6. result = self.function(*args, **kwargs)
  7. # 调用函数之后,做点什么
  8. # 并返回结果
  9. return result

装饰器简单应用

装饰器应用:

  • 数据验证
  • 日志
  • 缓存
  • 监控
  • 调试
  • 业务规则
  • 加密

1.日志打印器
2.时间计时器

  1. # 日志打印器
  2. def logger(func):
  3. def wrapper(*args, **kwargs):
  4. print("我准备开始计算:{}函数了".format(func.__name__))
  5. func(*args, **kwargs)
  6. print("我已执行完函数了!!!")
  7. return wrapper
  8. @logger
  9. def add(x, y):
  10. print('{} + {} = {}'.format(x, y, x+y))
  11. add(1, 2)
  12. ----------------
  13. #我准备开始计算:add函数了
  14. #1 + 2 = 3
  15. #我已执行完函数了!!!
  1. # 时间计时器
  2. def timer(func):
  3. def wrapper(*args, **kwargs):
  4. t1 = time.time()
  5. func(*args, **kwargs)
  6. t2 = time.time()
  7. result_time = t2 - t1
  8. print("执行函数一共花了{}".format(result_time))
  9. return wrapper
  10. import time
  11. @timer
  12. def want_sleep(sleep_time):
  13. time.sleep(sleep_time)
  14. want_sleep(10)
  15. ----------------------
  16. #执行函数一共花了10.008701086044312

带参数的函数装饰器

use_logging 是一个带参数的装饰器,我们可以理解为它是一个含有参数的闭包,当我们使用@use_logging(level=’warn’),
将业务函数foo1作为参数传入,传递到装饰器中

  1. # 带参数的装饰器
  2. def use_logging(level):
  3. def decorator(func):
  4. def wrapper(*args, **kwargs):
  5. if level == 'warn':
  6. print("warning !!!!!,{} is running".format(func.__name__))
  7. if level == 'info':
  8. print("{} is running".format(func.__name__))
  9. return func(*args, **kwargs)
  10. return wrapper
  11. return decorator
  12. @use_logging(level='warn')
  13. def foo1(name='foo'):
  14. print("i am {}".format(name))
  15. @use_logging(level='info')
  16. def foo2(name='FOO'):
  17. print("i am {}".format(name))
  18. foo1()
  19. foo2()
  20. ----------
  21. #warning !!!!!,foo1 is running
  22. #i am foo
  23. #foo2 is running
  24. #i am FOO

不带参数的类装饰器

实现类装饰器,必须实现__call_init两个内置函数,
init:用于接收被装饰的函数,
call:用于实现装饰逻辑

  1. class logger:
  2. def __init__(self, func):
  3. self.func = func
  4. def __call__(self, *args, **kwargs):
  5. print("[INFO]:the function {func}() is running".format(func=self.func.__name__))
  6. return self.func(*args, **kwargs)
  7. @logger
  8. def say(name):
  9. print("say {}!".format(name))
  10. say("zaygee")
  11. ---------------------
  12. #[INFO]:the function say() is running
  13. #say zaygee!

带参数的类装饰器

实现带参数的类装饰器,必须实现__call_init两个内置函数,
init:用于接收传入参数
call:用于接收被装饰函数+实现装饰逻辑

  1. # 带参数的类装饰器
  2. class loggering(object):
  3. def __init__(self, level='INFO'):
  4. """接收传入参数"""
  5. self.level = level
  6. def __call__(self, func):
  7. """接收函数并返回函数"""
  8. def warpper(*args, **kwargs):
  9. print("[{level}]: the function {func}() is runnig".format(level=self.level, func=func.__name__))
  10. func(*args, **kwargs)
  11. return warpper
  12. @loggering(level='WARNNING')
  13. def say(name):
  14. print("say {name}".format(name=name))
  15. say("i am zaygee")
  16. ------------------------
  17. #[WARNNING]: the function say() is runnig
  18. #say i am zaygee
  1. # 封装http请求日志打印
  2. class Logger:
  3. """带参数的类装饰器:http日志输出"""
  4. def __init__(self, method):
  5. self.method = method
  6. def __call__(self, func):
  7. def logging(*args, **kwargs):
  8. logger.info(f"用例开始执行{func.__name__}")
  9. res = func(*args, **kwargs)
  10. if self.method.lower() == 'post':
  11. logger.info(f'请求URl:{res.url}')
  12. else:
  13. logger.info('不是post请求')
  14. logger.info(f"用例结束执行{func.__name__}")
  15. return logging
  16. class BaseRequest:
  17. headers = {'Accept': 'application/json, text/javascript, */*; q=0.01',
  18. 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) '
  19. 'Chrome/81.0.4044.122 Safari/537.36',
  20. 'Connection': 'keep-alive',
  21. 'Content-Type': 'application/json'}
  22. def __init__(self):
  23. self.res = requests.session()
  24. self.res.headers = self.headers
  25. @Logger(method='post')
  26. def base_post(self, url, data=None, json=None, **kwargs):
  27. res = self.res.post(url=url, data=data, json=json, verify=True, **kwargs)
  28. return res
  29. 2021-07-11 22:51:55.818 | INFO | __main__:case_logging:105 - 用例开始执行
  30. 2021-07-11 22:51:55.818 | INFO | __main__:case_logging:107 - 1*4 = 4
  31. 2021-07-11 22:51:55.818 | INFO | __main__:case_logging:108 - 用例执行结束
  32. 2021-07-11 22:51:55.819 | INFO | __main__:logging:229 - 用例开始执行base_post
  33. 2021-07-11 22:51:56.058 | INFO | __main__:logging:232 - 请求URl:https://web/accounts/login
  34. 2021-07-11 22:51:56.059 | INFO | __main__:logging:235 - 用例结束执行base_post

wraps装饰器

@functools.wraps(wrapped, assigned=WRAPPER_ASSIGNMENTS, updated=WRAPPER_UPDATES)
装饰器极大的复用了代码,但是唯一的缺点就是原函数的元信息不见了 ,wraps本身也是一个装饰器,它能把原函数的元信息拷贝到装饰器函数中,这使得装饰器函数也有和原函数一样的元信息了

  1. # functions.wraps
  2. from functools import wraps
  3. def my_decorator(func):
  4. @wraps(func)
  5. def wrapper(*args, **kwargs):
  6. print("calling decorated function")
  7. return func(*args, **kwargs)
  8. return wrapper
  9. @my_decorator
  10. def example():
  11. """
  12. Dcostring
  13. """
  14. print("called decorated function")
  15. example()
  16. print(example.__name__)
  17. print(example.__doc__)
  18. -------------
  19. #calling decorated function
  20. #called decorated function
  21. #example
  22. # Dcostring

内置装饰器property

通常存在于类中,可以将函数定义成一个属性,属性的值就是该函数的返回,
当我们需要对属性做合法性的校验,可以考虑使用proprety装饰器

  1. class C:
  2. def __init__(self):
  3. self._x = None
  4. @property
  5. def x(self):
  6. """i am the 'x' property"""
  7. return self._x
  8. @x.setter
  9. def x(self, value):
  10. self._x = value
  11. @x.deleter
  12. def x(self):
  13. del self._x
  14. c = C()
  15. c.x = 10 # 设置属性
  16. print(c.x) # 查看属性
  17. del c.x # 删除属性
  18. -------------
  19. #10

例子:校验手机号的合法性

  1. class Phone:
  2. def __init__(self):
  3. self._x = None
  4. @property
  5. def x(self):
  6. """i am the 'x' property"""
  7. return self._x
  8. @x.setter
  9. def x(self, value):
  10. if not isinstance(value, int):
  11. # raise ValueError("请输入合法手机号")
  12. print("请输入合法手机号")
  13. else:
  14. self._x = value
  15. print("输入的手机:{}是合法的".format(self._x))
  16. @x.deleter
  17. def x(self):
  18. del self._x
  19. sj = Phone()
  20. sj.x = 'dfdfd'
  21. del sj.x
  22. sj.x = 13838839333
  23. ----------------
  24. #请输入合法手机号
  25. #输入的手机:13838839333是合法的

多装饰器装饰的执行顺序

执行顺序为:至上而下,再至下而上 执行

  1. from logzero import logger
  2. import time
  3. # here put the import lib
  4. def my_decorator(func):
  5. def wrapper(*args, **kwargs):
  6. logger.info(f"调用业务代码前执行")
  7. result = func(*args, **kwargs)
  8. logger.info(f"调用业务代码后执行")
  9. return result
  10. return wrapper
  11. def decorator_time(func):
  12. def wrapper(*args, **kwargs):
  13. start = time.time()
  14. logger.info(f"开始执行: {start}")
  15. result = func(*args, **kwargs)
  16. logger.info(f"执行代码耗时: {time.time()-start}")
  17. return result
  18. return wrapper
  19. def decorator_with_params(params):
  20. def decorator(func):
  21. def wrapper(*args, **kwargs):
  22. if params != 1:
  23. logger.info("params=1")
  24. else:
  25. logger.info("params is not eq 1")
  26. return func(*args, **kwargs)
  27. logger.info("带参数decorator执行完毕")
  28. return wrapper
  29. return decorator
  30. @decorator_time
  31. @my_decorator
  32. @decorator_with_params(1)
  33. def test_decorator():
  34. logger.info("执行业务代码")
  35. if __name__ == "__main__":
  36. test_decorator()
  37. """
  38. [I 220112 23:30:23 test1:23] 开始执行: 1642001423.521113
  39. [I 220112 23:30:23 test1:13] 调用业务代码前执行
  40. [I 220112 23:30:23 test1:35] params is not eq 1
  41. [I 220112 23:30:23 test1:46] 执行业务代码
  42. [I 220112 23:30:23 test1:15] 调用业务代码后执行
  43. [I 220112 23:30:23 test1:25] 执行代码耗时: 0.00032520294189453125
  44. """

使用decorator轻松实现装饰器

  1. from decorator import decorator
  2. # @decorator
  3. # def decoration(func, *args, **kwargs):
  4. # """decorator装饰 生成一个装饰器函数"""
  5. # print("Ready to run func " + func.__name__)
  6. # func(*args, **kwargs)
  7. # print("successful to run func " + func.__name__)
  8. # 等同于如下实现
  9. def decoration(func, *args, **kwargs):
  10. def wrapper(*args, **kwargs):
  11. print("Ready to run func " + func.__name__)
  12. func(*args, **kwargs)
  13. print("successful to run func " + func.__name__)
  14. return wrapper
  15. @decoration
  16. def demo():
  17. print("run the demo task")
  18. demo()
  19. print(demo.__name__) # wrapper
  20. @decorator
  21. def decoration_with_params(func, values=1, *args, **kwargs):
  22. results = func(*args, **kwargs)
  23. if values == 1:
  24. print('values = 1')
  25. return results
  26. else:
  27. print('values ! = 1')
  28. return '3'
  29. @decoration_with_params(values=1)
  30. def demo_2():
  31. print("run the demo 2 task")
  32. return 1
  33. print(demo_2())
  34. print(demo_2.__name__) # demo_2

参考自:https://zhuanlan.zhihu.com/p/65968462https://foofish.net/decorator.html