这篇文章将为大家详细讲解有关Python中使用装饰器的各种技巧,文章内容质量较高,因此小编分享给大家做个参考,希望大家阅读完这篇文章后对相关知识有一定的了解。
<>强装饰器(装饰),强>是Python里的一种特殊工具,它为我们提供了一种在函数外部修改函数的灵活能力。它有点像一顶画着独一无二,@ 符号的神奇帽子,只要将它戴在函数头顶上,就能悄无声息的改变函数本身的行为。
你可能已经和装饰器打过不少交道了。在做面向对象编程时,我们就经常会用到,@staticmethod 和,@classmethod 两个内置装饰器。此外,如果你接触过,click 模块,就更不会对装饰器感到陌生.click最为人所称道的参数定义接口,@click.option(…),就是利用装饰器实现的。
除了用装饰器,我们也经常需要自己写一些装饰器。在这篇文章里,我将从,最佳实践,和,常见错误,两个方面,来与你分享有关装饰器的一些小知识。
<强> 1。尝试用类来实现装饰器强>
绝大多数装饰器都是基于函数和,闭包,实现的,但这并非制造装饰器的唯一方式。事实上,Python对某个对象是否能通过装饰器(@decorator)形式使用只有一个要求:装饰必须是一个“可被调用(调用)的对象。
#,使用,callable 可以检测某个对象是否”可被调用” 在祝辞祝辞,def foo():通过 … 在祝辞祝辞,类型(foo) & lt; class & # 39;函数# 39;比; 祝辞祝辞祝辞,可调用foo () 真正的
函数自然是“可被调用”的对象。但除了函数外,我们也可以让任何一个类(类)变得”可被调用”(调用)。办法很简单,只要自定义类的,__call__ 魔法方法即可。
class Foo: ,,,def __call__(自我): ,,,,,,,印刷(“你好,,__call___") 时间=foo Foo () #,输出:真实 打印(可调用foo ()) #,调用,foo 实例 #,输出:你好,__call__ foo ()
基于这个特性,我们可以很方便的使用类来实现装饰器。
下面这段代码,会定义一个名为,<强> @delay(持续时间),强>的装饰器,使用它装饰过的函数在每次执行前,都会等待额外的,duration 秒。同时,我们也希望为用户提供无需等待马上执行的,eager_call 接口。
import 时间 import functools class DelayFunc: ,,,def __init__(自我,,,时间,函数): ,,,,,,,self.duration =,持续时间 ,,,,,,,self.func =函数 def __call__(自我,,* args,, * * kwargs): ,,,,,,,印刷(f # 39; Wait  for {self.duration},秒……& # 39;) ,,,,,,,time . sleep (self.duration) ,,,,,,,return self.func (* args,, * * kwargs) def eager_call(自我,,* args,, * * kwargs): ,,,,,,,印刷(& # 39;Call  without 延迟# 39;) ,,,,,,,return self.func (* args,, * * kwargs) def 延迟(时间): ,,,,,,装饰器:推迟某个函数的执行。同时提供,.eager_call 方法立即执行 ,,,,,, ,,,#,此处为了避免定义额外函数,直接使用,functools.partial 帮助构造 ,,,#,DelayFunc 实例 ,,,return functools.partial (DelayFunc,持续时间) 如何使用装饰器的样例代码: @delay(持续时间=2) def 添加(a, b): ,,,return a + b #,这次调用将会延迟,2秒 添加(1,2) #,这次调用将会立即执行 add.eager_call (1, 2)
<强> @delay(持续时间),>强就是一个基于类来实现的装饰器。当然,如果你非常熟悉Python里的函数和闭包,上面的,delay 装饰器其实也完全可以只用函数来实现,所以,为什么我们要用类来做这件事呢?
<强>与纯函数相比,我觉得使用类实现的装饰器在特定场景下有几个优势:强>
?实现有状态的装饰器时,操作类属性比操作闭包内变量更符合直觉,不易出错
?实现为函数扩充接口的装饰器时,使用类包装函数,比直接为函数对象追加属性更易于维护
?更容易实现一个同时兼容装饰器与上下文管理器协议的对象(参考,unitest.mock.patch)
<强> 2。使用打包模块编写更扁平的装饰器强>
在写装饰器的过程中,你有没有碰到过什么不爽的事情?不管你有没有,反正我有。我经常在写代码的时候,被下面两件事情搞得特别难受:
1。实现带参数的装饰器时,层层嵌套的函数代码特别难写、难读
2。因为函数和类方法的不同,为前者写的装饰器经常没法直接套用在后者上