Python中使用装饰器的各种技巧

  介绍

这篇文章将为大家详细讲解有关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。因为函数和类方法的不同,为前者写的装饰器经常没法直接套用在后者上

Python中使用装饰器的各种技巧