谁可以作为装饰器(可以将谁编写成装饰器):
- 函数
- 方法
- 实现了__call__的可调用类
装饰器可以去装饰谁(谁可以被装饰):
- 函数
- 方法
- 类
基础:函数装饰器的表现方式
假如你已经定义了一个函数funcA(),在准备定义函数funcB()的时候,如果写成下面的格式:
@funcA
def funcB():...
表示用函数funcA()装饰函数funcB()。当然,也可以认为是funcA包装函数funcB。它等价于:
def funcB():...
funcB = funcA(funcB)
也就是说,将函数funcB作为函数funcA的参数,funcA会重新返回另一个可调用的对象(比如函数)并赋值给funcB。
所以,funcA要想作为函数装饰器,需要接收函数作为参数,并且返回另一个可调用对象(如函数)。例如:
def funcA(F):
...
...
return Callable
我是一名python开发工程师,整理了一套python的学习资料,从基础的python脚本到web开发、爬虫、
数据分析、数据可视化、机器学习、面试真题等。想要的可以进群:688244617免费领取
注意,函数装饰器返回的可调用对象并不一定是原始的函数F,可以是任意其它可调用对象,比如另一个函数。但最终,这个返回的可调用对象都会被赋值给被装饰的函数变量(上例中的funcB)。
函数可以同时被多个装饰器装饰,后面的装饰器以前面的装饰器处理结果为基础进行处理:
@decorator1
@decorator2
def func():...
# 等价于
func = decorator1(decorator2(func))
当调用被装饰后的funcB时,将自动将funcB进行装饰,并调用装饰后的对象。所以,下面是等价的调用方式:
funcB() # 调用装饰后的funcB
funcA(funcB)()
了解完函数装饰器的表现后,大概也能猜到了,装饰器函数可以用来扩展、增强另外一个函数。实际上,内置函数中staticmethod()、classmethod()和property()都是装饰器函数,可以用来装饰其它函数。
两个简单的例子
例如,函数f()返回一些字符串,现在要将它的返回结果转换为大写字母。可以定义一个函数装饰器来增强函数f()。
def toupper(func):
def wrapper(*args, **kwargs):
result = func(*args, **kwargs)
return result.upper()
return wrapper
@toupper
def f(x: str): # 等价于f = toupper(f)
return x
res = f("abcd")
print(res)
上面toupper()装饰f()后,调用f("abcd")的时候,等价于执行toupper(f)("abcd"),参数"abcd"传递给装饰器中的wrapper()中的*args,在wrapper中又执行了f("abcd"),使得原本属于f()的整个过程都完整了,最后返回result.upper(),这部分是对函数f()的扩展部分。
注意,上面的封装函数wrapper()中使用了*args **kwargs,是为了确保任意参数的函数都能正确执行下去。
再比如要计算一个函数autodown()的执行时长,可以额外定义一个函数装饰器timecount()。

执行结果:
autodown 0.004986763000488281
autodown 0.05684685707092285
autodown 0.5336081981658936
上面wrapper()中的return是多余的,是因为这里装饰的autodown()函数自身没有返回值。但却不应该省略这个return,因为timecount()可以去装饰其它可能有返回值的函数。
觉得文章还不错的话不妨收藏起来慢慢看,有任何建议或看法欢迎大家在评论区分享讨论!

494

被折叠的 条评论
为什么被折叠?



