Python :使用装饰器的技巧

本文介绍了Python装饰器的使用技巧,包括最佳实践和常见错误。最佳实践中,建议尝试用类实现装饰器,以实现有状态的装饰器和扩展函数接口,并推荐使用wrapt模块编写更扁平的装饰器。常见错误部分,澄清了装饰器并非装饰器模式,提醒在编写装饰器时使用functools.wraps()保持函数签名,以及在修改外层变量时使用nonlocal关键字。
摘要由CSDN通过智能技术生成

前言

装饰器(Decorator) 是 Python 里的一种特殊工具,它为我们提供了一种在函数外部修改函数的灵活能力。它有点像一顶画着独一无二 @ 符号的神奇帽子,只要将它戴在函数头顶上,就能悄无声息的改变函数本身的行为。

你可能已经和装饰器打过不少交道了。在做面向对象编程时,我们就经常会用到 @staticmethod和 @classmethod 两个内置装饰器。此外,如果你接触过 click 模块,就更不会对装饰器感到陌生。click 最为人所称道的参数定义接口 @click.option(...) 就是利用装饰器实现的。

除了用装饰器,我们也经常需要自己写一些装饰器。在这篇文章里,我将从 最佳实践 和 常见错误两个方面,来与你分享有关装饰器的一些小知识。

最佳实践

1. 尝试用类来实现装饰器

绝大多数装饰器都是基于函数和 闭包 实现的,但这并非制造装饰器的唯一方式。事实上,Python 对某个对象是否能通过装饰器( @decorator)形式使用只有一个要求:decorator 必须是一个“可被调用(callable)的对象

 
  1. # 使用 callable 可以检测某个对象是否“可被调用”

  2. >>> def foo(): pass

  3. ...

  4. >>> type(foo)

  5. <class "function">

  6. >>> callable(foo)

  7. True

函数自然是“可被调用”的对象。但除了函数外,我们也可以让任何一个类(class)变得“可被调用”(callable)。办法很简单,只要自定义类的 __call__ 魔法方法即可。

 
  1. class Foo:

  2. def __call__(self):

  3. print("Hello, __call___")

  4.  

  5. foo = Foo()

  6.  

  7. # OUTPUT: True

  8. print(callable(foo))

  9. # 调用 foo 实例

  10. # OUTPUT: Hello, __call__

  11. foo()

基于这个特性,我们可以很方便的使用类来实现装饰器。

下面这段代码,会定义一个名为 @delay(duration) 的装饰器,使用它装饰过的函数在每次执行前,都会等待额外的 duration 秒。同时,我们也希望为用户提供无需等待马上执行的 eager_call 接口。

 
  1. import time

  2. import functools

  3.  

  4.  

  5. class DelayFunc:

  6. def __init__(self, duration, func):

  7. self.duration = duration

  8. self.func = func

  9.  

  10. def __call__(self, *args, **kwargs):

  11. print(f"Wait for {self.duration} seconds...")

  12. time.sleep(self.duration)

  13. return self.func(*args, **kwargs)

  14.  

  15. def eager_call(self, *args, **kwargs):

  16. print("Call without delay")

  17. return self.func(*args, **kwargs)

  18.  

  19.  

  20. def delay(duration):

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值