前段时间看水货米洛杂货 都在更新装饰器相关内容,我就奇怪了,怎么都在更新这个。莫非他们和这位同学一样出去面试了。哈哈,懂的都懂。

嗯,今天咱们站在几位前辈的肩上,复习一下装饰器的用法。

装饰器 - 图1

装饰器的含义:

  1. 装饰器函数就是一个闭包。之前看到我同学写的那些Java代码,有好多带@的,他说叫注解,后来才了解这和python的装饰器是一个道理。
  2. python的语法糖,隐藏了底层的实现,开发者只需要吃糖。

装饰器的作用:

装饰器是用来装饰函数的,在不改变函数的定义和调用的条件下,能够给函数扩展功能。

装饰器的场景:

日志记录,用例重跑,异常捕获(UI用例执行异常自动截图),给变量加锁,数据的清理(unittest种的setup、teardown),时间统计(用例执行时间)

装饰器的初体验

  1. def outer(func):
  2. print('我开始执行了')
  3. def inner():
  4. print('我进入decorate2了')
  5. func()
  6. return inner
  7. @outer
  8. def demo_decorate():
  9. print('我是一个小demo')
  10. if __name__ == '__main__':
  11. demo_decorate()

打个断点,我们发现这玩意是先进行装饰,等调用的时候才会真正执行。大家可以自行试下。

装饰器 - 图2

装饰器 - 图3

装饰器 - 图4

装饰器 - 图5

手动实现装饰器

打个断点,看下具体实现吧。

  1. def set_fun(func):
  2. def call_func():
  3. print("====执行内部函数===")
  4. func()
  5. return call_func
  6. def test_demo():
  7. print("=======test_demo========")
  8. if __name__ == '__main__':
  9. test = set_fun(test_demo)
  10. test()
  11. # 执行的顺序如下:
  12. 1. set_fun对象
  13. 2. test对象
  14. set_fun(test_demo) #等号的右边
  15. test ===>call_func #等号的左边
  16. 3. test() ====》call_func() func()本质上执行的是test_demo()

装饰器装饰不同类型的函数

  1. #装饰器没有参数,没有返回值
  2. def set_fun(func):
  3. def call_func():
  4. print("====执行内部函数===")
  5. func()
  6. return call_func
  7. @set_fun
  8. def test_demo():
  9. print("=======test_demo========")
  10. if __name__ == '__main__':
  11. test_demo()
  1. #装饰器有参数,没有返回值
  2. def set_fun(func):
  3. def call_func(num):
  4. print("====执行内部函数===")
  5. print("=========num======",num)
  6. func(num)
  7. return call_func
  8. @set_fun
  9. def test_demo(num):
  10. print("=======test_demo========")
  11. print(num)
  12. if __name__ == '__main__':
  13. test_demo(1)
  1. # 装饰器没有参数,有返回值
  2. def set_fun(func):
  3. def call_func(num):
  4. print("====执行内部函数===")
  5. print("=========num======", num)
  6. ret = func(num)
  7. return ret
  8. return call_func
  9. @set_fun
  10. def test_demo(num):
  11. print("=======test_demo========")
  12. print(num)
  13. if __name__ == '__main__':
  14. test_demo(1)
  1. # 万能装饰器,可以装饰所有函数
  2. def set_fun(func):
  3. def call_func(*args, **kwargs):
  4. print("====执行内部函数===")
  5. ret = func(*args, **kwargs)
  6. return ret
  7. return call_func
  8. @set_fun
  9. def test_demo(*args, **kwargs):
  10. print("=======test_demo========")
  11. print(args)
  12. print(kwargs)
  13. @set_fun
  14. def test_demo2(*args, **kwargs):
  15. print("=======test_demo2========")
  16. print(args)
  17. print(kwargs)
  18. @set_fun
  19. def test_demo3(*args, **kwargs):
  20. print("=======test_demo3========")
  21. print(args)
  22. print(kwargs)
  23. if __name__ == '__main__':
  24. test_demo()
  25. test_demo2(1, 2)
  26. test_demo3(1, 2, 3, 4, a=10, b=20)

装饰器 - 图6

类装饰器

通过这个,你会看的更清楚,函数的确被当做参数使用了,否则下面的代码将会报错。

  1. # 类对象也可以当做装饰器函数
  2. class Person(object):
  3. def __init__(self, func):
  4. self.func = func
  5. def __call__(self, *args, **kwargs):
  6. print("self.func",self.func)
  7. print(args)
  8. print(kwargs)
  9. # print("我被执行啦")
  10. self.func()
  11. # 类装饰器
  12. @Person # test_demo = Person(test_demo) # 函数当做参数使用 如果__init__只有一个参数:TypeError: __init__() takes 1 positional argument but 2 were given
  13. def test_demo():
  14. print("---- init-------")
  15. test_demo()
  16. print("==================")
  17. test_demo(1, 2)

装饰器 - 图7

函数被多个装饰器装饰

记住这个结论 : 装饰的过程是从内到外,执行的过程是从外到内。推导起来也不难,就是容易给自己绕进去。

  1. def message(func):
  2. print("====message:message_pay========")
  3. def message_pay():
  4. print("======message_pay======")
  5. func()
  6. return message_pay
  7. def gesture(func):
  8. print("======gesture:gesture_pay=====")
  9. def gesture_pay():
  10. print("======gesture_pay======")
  11. func()
  12. return gesture_pay
  13. # 在支付之前需要完成短信安全验证 和 手势密码安全验证
  14. @message
  15. @gesture
  16. def pay():
  17. print("----正在支付中----")
  18. pay()
  19. # 装饰的过程是从内到外
  20. # 执行的过程是从外到内
  21. """
  22. 22行: 遇到了@message 装饰器,不会执行需要等待, 等待被装饰的函数定义好
  23. 23行: 又遇到@gesture装饰器, 不会执行需要等待, 等待被装饰的函数定义好
  24. 24行: 定义了 被装饰的函数pay
  25. 23行: 底层实现 pay = gesture(pay)
  26. 23.1: 等于的右边 gesture(pay) gesture.func --> pay
  27. 23.2: 等于的右边 pay等于gesture(pay)返回值gesture_pay pay --> gesture_pay
  28. 22行: 底层实现 pay = message(pay)
  29. 22.1: 等于的右边 message(pay) message.func --> gesture_pay
  30. 22.2: 等于的右边 pay等于message(pay)返回值message_pay pay --> message_pay
  31. 27行:
  32. pay()
  33. """

跟大佬学到一种

跟米洛(博主:米洛的测开日记)学到的一招,真的是大开眼界。

  1. def run(times):
  2. '''这个callable方法是核心'''
  3. if callable(times):
  4. def wrapper(*args, **kwargs):
  5. for i in range(5):
  6. times(*args, **kwargs)
  7. print(f"运行的第{i}次")
  8. return wrapper
  9. else:
  10. def decorator(func):
  11. def wrapper(*args, **kwargs):
  12. for i in range(times):
  13. func(*args, **kwargs)
  14. print(f"运行的第{i}次")
  15. return wrapper
  16. return decorator
  17. '''没有加参数,callable是False,会运行5次'''
  18. @run
  19. def func():
  20. print("func运行了")
  21. '''此时callable是True,则会按照传参运行相应次数'''
  22. @run(1)
  23. def func1():
  24. print("func1运行了")
  25. if __name__ == "__main__":
  26. func()
  27. print("---我是分割线---")
  28. func1()

装饰器 - 图8