前段时间看水货
,米洛
,杂货
都在更新装饰器相关内容,我就奇怪了,怎么都在更新这个。莫非他们和这位同学一样出去面试了。哈哈,懂的都懂。
嗯,今天咱们站在几位前辈的肩上,复习一下装饰器的用法。
装饰器的含义:
- 装饰器函数就是一个闭包。之前看到我同学写的那些
Java
代码,有好多带@
的,他说叫注解,后来才了解这和python
的装饰器是一个道理。 - python的语法糖,隐藏了底层的实现,开发者只需要吃糖。
装饰器的作用:
装饰器是用来装饰函数的,在不改变函数的定义和调用的条件下,能够给函数扩展功能。
装饰器的场景:
日志记录,用例重跑,异常捕获(UI用例执行异常自动截图),给变量加锁,数据的清理(unittest种的setup、teardown),时间统计(用例执行时间)
装饰器的初体验
def outer(func):
print('我开始执行了')
def inner():
print('我进入decorate2了')
func()
return inner
@outer
def demo_decorate():
print('我是一个小demo')
if __name__ == '__main__':
demo_decorate()
打个断点,我们发现这玩意是先进行装饰,等调用的时候才会真正执行。大家可以自行试下。
手动实现装饰器
打个断点,看下具体实现吧。
def set_fun(func):
def call_func():
print("====执行内部函数===")
func()
return call_func
def 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_fun
def 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_fun
def 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 ret
return call_func
@set_fun
def 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 ret
return call_func
@set_fun
def test_demo(*args, **kwargs):
print("=======test_demo========")
print(args)
print(kwargs)
@set_fun
def test_demo2(*args, **kwargs):
print("=======test_demo2========")
print(args)
print(kwargs)
@set_fun
def 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 = func
def __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 given
def 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_pay
def gesture(func):
print("======gesture:gesture_pay=====")
def gesture_pay():
print("======gesture_pay======")
func()
return gesture_pay
# 在支付之前需要完成短信安全验证 和 手势密码安全验证
@message
@gesture
def pay():
print("----正在支付中----")
pay()
# 装饰的过程是从内到外
# 执行的过程是从外到内
"""
22行: 遇到了@message 装饰器,不会执行需要等待, 等待被装饰的函数定义好
23行: 又遇到@gesture装饰器, 不会执行需要等待, 等待被装饰的函数定义好
24行: 定义了 被装饰的函数pay
23行: 底层实现 pay = gesture(pay)
23.1: 等于的右边 gesture(pay) gesture.func --> pay
23.2: 等于的右边 pay等于gesture(pay)返回值gesture_pay pay --> gesture_pay
22行: 底层实现 pay = message(pay)
22.1: 等于的右边 message(pay) message.func --> gesture_pay
22.2: 等于的右边 pay等于message(pay)返回值message_pay pay --> message_pay
27行:
pay()
"""
跟大佬学到一种
跟米洛(博主:米洛的测开日记
)学到的一招,真的是大开眼界。
def run(times):
'''这个callable方法是核心'''
if callable(times):
def wrapper(*args, **kwargs):
for i in range(5):
times(*args, **kwargs)
print(f"运行的第{i}次")
return wrapper
else:
def decorator(func):
def wrapper(*args, **kwargs):
for i in range(times):
func(*args, **kwargs)
print(f"运行的第{i}次")
return wrapper
return decorator
'''没有加参数,callable是False,会运行5次'''
@run
def func():
print("func运行了")
'''此时callable是True,则会按照传参运行相应次数'''
@run(1)
def func1():
print("func1运行了")
if __name__ == "__main__":
func()
print("---我是分割线---")
func1()