装饰器是Python中一种强大的语法糖,它允许我们在不修改原函数代码的情况下,为函数添加额外的功能。装饰器本质上是一个可调用对象,它接受一个函数作为输入,并返回一个新的函数。装饰器的使用方式非常灵活,既可以不带参数使用,也可以带参数使用
import sysprint('python 版本:',sys.version.split('|')[0])#python 版本: 3.11.11我们先来看一个简单的函数装饰器实现:
import timedefdelayed_start(func=None, *, duration=1):defdecorator(_func):defwrapper(*args, **kwargs): print(f"Wait for {duration} seconds before starting...") time.sleep(duration)return _func(*args, **kwargs)return wrapperif func isNone:return decoratorelse:return decorator(func)这个装饰器可以以两种方式使用:
delayed_start@delayed_startdefhello_no_arg(name="datashare"): print("from hello_no_arg, param name =", name)等价于:
hello_no_arg = delayed_start(hello_no_arg)@delayed_start(duration=2)defhello_with_arg(name="datashare"): print("from hello_with_arg, param name =", name)等价于:
hello_with_arg = delayed_start(duration=2)(hello_with_arg)delayed_start函数的巧妙之处在于它通过检查 func参数是否为 None来判断装饰器的使用方式:
func不是 None,说明是不带参数使用,直接返回 decorator(func)func是 None,说明是带参数使用,返回 decorator函数等待接收真正的函数参数类装饰器通过实现 __call__方法来实现装饰器的功能。下面是带参数和不带参数的类装饰器实现:
from functools import wrapsimport timeclassTimer:def__init__(self, func=None, *, print_args=False): self.func = func self.print_args = print_argsdef__call__(self, *args, **kwargs):# 情况 1:@Timer(print_args=True)# 第一次 __call__,args[0] 是函数if self.func isNone: func = args[0]return self._decorate(func)# 情况 2:@Timer# 或者已经绑定好函数,真正执行return self._decorate(self.func)(*args, **kwargs)def_decorate(self, func): @wraps(func)defwrapper(*args, **kwargs): st = time.perf_counter() ret = func(*args, **kwargs)if self.print_args: print(f'"{func.__name__}", args: {args}, kwargs: {kwargs}') print(f"time cost: {time.perf_counter() - st:.4f} seconds")return retreturn wrapper@Timerdefcompute(x): time.sleep(1)return x * 2当类装饰器不带参数使用时,Python会创建 Timer类的实例,并将 compute函数传递给 __init__方法。此时 self.func就是 compute函数。当我们调用 compute(10)时,实际上是调用 Timer实例的 __call__方法
@Timer(print_args=True)defcompute2(x): time.sleep(1)return x * 3当类装饰器带参数使用时,Python会先调用 Timer(print_args=True)创建实例,此时 self.func为 None。然后用这个实例去装饰 compute2函数,这相当于调用 Timer实例的 __call__方法,并将 compute2作为参数传入
| 代码简洁性 | ||
| 状态管理 | ||
| 可扩展性 | ||
| 可读性 |
Timer装饰器,用于测量函数执行时间functools.wraps装饰器来保留原函数的元数据(如函数名、文档字符串等)Python装饰器是一个强大而灵活的特性,它允许我们以非侵入式的方式增强函数的功能。通过掌握函数装饰器和类装饰器的实现方式,以及如何实现带参数和不带参数的装饰器,我们可以编写出更加通用和可重用的代码。无论是简单的函数增强还是复杂的状态管理,装饰器都能提供优雅的解决方案
理解装饰器的工作原理不仅有助于我们编写更好的装饰器,还能加深我们对Python函数式编程和元编程的理解,是成为高级Python开发者的重要一步
以上是自己实践中遇到的一些问题,分享出来供大家参考学习,欢迎关注微信公众号:DataShare ,不定期分享干货
整理的一个资源共享库:
https://github.com/DataShare-duo/datashare_data