前段时间看水货,米洛,杂货 都在更新装饰器相关内容,我就奇怪了,怎么都在更新这个。莫非他们和这位同学一样出去面试了。哈哈,懂的都懂。
嗯,今天咱们站在几位前辈的肩上,复习一下装饰器的用法。

装饰器的含义:
- 装饰器函数就是一个闭包。之前看到我同学写的那些
Java代码,有好多带@的,他说叫注解,后来才了解这和python的装饰器是一个道理。 - python的语法糖,隐藏了底层的实现,开发者只需要吃糖。
装饰器的作用:
装饰器是用来装饰函数的,在不改变函数的定义和调用的条件下,能够给函数扩展功能。
装饰器的场景:
日志记录,用例重跑,异常捕获(UI用例执行异常自动截图),给变量加锁,数据的清理(unittest种的setup、teardown),时间统计(用例执行时间)
装饰器的初体验
def outer(func):print('我开始执行了')def inner():print('我进入decorate2了')func()return inner@outerdef demo_decorate():print('我是一个小demo')if __name__ == '__main__':demo_decorate()
打个断点,我们发现这玩意是先进行装饰,等调用的时候才会真正执行。大家可以自行试下。




手动实现装饰器
打个断点,看下具体实现吧。
def set_fun(func):def call_func():print("====执行内部函数===")func()return call_funcdef test_demo():print("=======test_demo========")if __name__ == '__main__':test = set_fun(test_demo)test()# 执行的顺序如下:1. set_fun对象2. test对象set_fun(test_demo) #等号的右边test ===>call_func #等号的左边3. test() ====》call_func() func()本质上执行的是test_demo()
装饰器装饰不同类型的函数
#装饰器没有参数,没有返回值def set_fun(func):def call_func():print("====执行内部函数===")func()return call_func@set_fundef test_demo():print("=======test_demo========")if __name__ == '__main__':test_demo()
#装饰器有参数,没有返回值def set_fun(func):def call_func(num):print("====执行内部函数===")print("=========num======",num)func(num)return call_func@set_fundef test_demo(num):print("=======test_demo========")print(num)if __name__ == '__main__':test_demo(1)
# 装饰器没有参数,有返回值def set_fun(func):def call_func(num):print("====执行内部函数===")print("=========num======", num)ret = func(num)return retreturn call_func@set_fundef test_demo(num):print("=======test_demo========")print(num)if __name__ == '__main__':test_demo(1)
# 万能装饰器,可以装饰所有函数def set_fun(func):def call_func(*args, **kwargs):print("====执行内部函数===")ret = func(*args, **kwargs)return retreturn call_func@set_fundef test_demo(*args, **kwargs):print("=======test_demo========")print(args)print(kwargs)@set_fundef test_demo2(*args, **kwargs):print("=======test_demo2========")print(args)print(kwargs)@set_fundef test_demo3(*args, **kwargs):print("=======test_demo3========")print(args)print(kwargs)if __name__ == '__main__':test_demo()test_demo2(1, 2)test_demo3(1, 2, 3, 4, a=10, b=20)

类装饰器
通过这个,你会看的更清楚,函数的确被当做参数使用了,否则下面的代码将会报错。
# 类对象也可以当做装饰器函数class Person(object):def __init__(self, func):self.func = funcdef __call__(self, *args, **kwargs):print("self.func",self.func)print(args)print(kwargs)# print("我被执行啦")self.func()# 类装饰器@Person # test_demo = Person(test_demo) # 函数当做参数使用 如果__init__只有一个参数:TypeError: __init__() takes 1 positional argument but 2 were givendef test_demo():print("---- init-------")test_demo()print("==================")test_demo(1, 2)

函数被多个装饰器装饰
记住这个结论 : 装饰的过程是从内到外,执行的过程是从外到内。推导起来也不难,就是容易给自己绕进去。
def message(func):print("====message:message_pay========")def message_pay():print("======message_pay======")func()return message_paydef gesture(func):print("======gesture:gesture_pay=====")def gesture_pay():print("======gesture_pay======")func()return gesture_pay# 在支付之前需要完成短信安全验证 和 手势密码安全验证@message@gesturedef pay():print("----正在支付中----")pay()# 装饰的过程是从内到外# 执行的过程是从外到内"""22行: 遇到了@message 装饰器,不会执行需要等待, 等待被装饰的函数定义好23行: 又遇到@gesture装饰器, 不会执行需要等待, 等待被装饰的函数定义好24行: 定义了 被装饰的函数pay23行: 底层实现 pay = gesture(pay)23.1: 等于的右边 gesture(pay) gesture.func --> pay23.2: 等于的右边 pay等于gesture(pay)返回值gesture_pay pay --> gesture_pay22行: 底层实现 pay = message(pay)22.1: 等于的右边 message(pay) message.func --> gesture_pay22.2: 等于的右边 pay等于message(pay)返回值message_pay pay --> message_pay27行:pay()"""
跟大佬学到一种
跟米洛(博主:米洛的测开日记)学到的一招,真的是大开眼界。
def run(times):'''这个callable方法是核心'''if callable(times):def wrapper(*args, **kwargs):for i in range(5):times(*args, **kwargs)print(f"运行的第{i}次")return wrapperelse:def decorator(func):def wrapper(*args, **kwargs):for i in range(times):func(*args, **kwargs)print(f"运行的第{i}次")return wrapperreturn decorator'''没有加参数,callable是False,会运行5次'''@rundef func():print("func运行了")'''此时callable是True,则会按照传参运行相应次数'''@run(1)def func1():print("func1运行了")if __name__ == "__main__":func()print("---我是分割线---")func1()

