首页 > 应用开发 > > python装饰器:函数装饰器的表现方式
谁可以作为装饰器(可以将谁编写成装饰器):
函数
方法
实现了__call__的可调用类
装饰器可以去装饰谁(谁可以被装饰):
函数
方法
类
基础:函数装饰器的表现方式
假如你已经定义了一个函数funcA(),在准备定义函数funcB()的时候,如果写成下面的格式:
1 @funcA
2 def funcB():...
表示用函数funcA()装饰函数funcB()。当然,也可以认为是funcA包装函数funcB。它等价于:
1 def funcB():...
2
3 funcB = funcA(funcB)
也就是说,将函数funcB作为函数funcA的参数,funcA会重新返回另一个可调用的对象(比如函数)并赋值给funcB。
所以,funcA要想作为函数装饰器,需要接收函数作为参数,并且返回另一个可调用对象(如函数)。例如:
1 def funcA(F):
2 ...
3 ...
4 return Callable
注意,函数装饰器返回的可调用对象并不一定是原始的函数F,可以是任意其它可调用对象,比如另一个函数。但最终,这个返回的可调用对象都会被赋值给被装饰的函数变量(上例中的funcB)。
函数可以同时被多个装饰器装饰,后面的装饰器以前面的装饰器处理结果为基础进行处理:
1 @decorator1
2 @decorator2
3 def func():...
4
5 # 等价于
6 func = decorator1(decorator2(func))
当调用被装饰后的funcB时,将自动将funcB进行装饰,并调用装饰后的对象。所以,下面是等价的调用方式:
1 funcB() # 调用装饰后的funcB
2 funcA(funcB)()
了解完函数装饰器的表现后,大概也能猜到了,装饰器函数可以用来扩展、增强另外一个函数。实际上,内置函数中staticmethod()、classmethod()和property()都是装饰器函数,可以用来装饰其它函数。
两个简单的例子
例如,函数f()返回一些字符串,现在要将它的返回结果转换为大写字母。可以定义一个函数装饰器来增强函数f()。
1 def toupper(func):
2 def wrapper(*args, **kwargs):
3 result = func(*args, **kwargs)
4 return result.upper()
5 return wrapper
6 @toupper
7 def f(x: str): # 等价于f = toupper(f)
8 return x
9 res = f("abcd")
10 print(res)
上面toupper()装饰f()后,调用f("abcd")的时候,等价于执行toupper(f)("abcd"),参数"abcd"传递给装饰器中的wrapper()中的*args,在wrapper中又执行了f("abcd"),使得原本属于f()的整个过程都完整了,最后返回result.upper(),这部分是对函数f()的扩展部分。
注意,上面的封装函数wrapper()中使用了*args **kwargs,是为了确保任意参数的函数都能正确执行下去。
再比如要计算一个函数autodown()的执行时长,可以额外定义一个函数装饰器timecount()。
执行结果:
1 autodown 0.004986763000488281
2 autodown 0.05684685707092285
3 autodown 0.5336081981658936
上面wrapper()中的return是多余的,是因为这里装饰的autodown()函数自身没有返回值。但却不应该省略这个return,因为timecount()可以去装饰其它可能有返回值的函数。
觉得文章还不错的话不妨收藏起来慢慢看,有任何建议或看法欢迎大家在评论区分享讨论!
来自 “ ITPUB博客 ” ,链接:https://blog.itpub.net/4822/viewspace-3167132/ ,如需转载,请注明出处,否则将追究法律责任。