1 什么是类装饰器
在了解类装饰器之前,建议大家先了解装饰器的概念。
装饰器知识快速入门链接
类装饰器是 Python 中的一种特殊类型的装饰器,它是一个类而不是一个函数。与函数装饰器不同,类装饰器可以在运行时接收参数并返回一个可调用的对象,而不是直接替换被装饰的函数。
类装饰器的语法是在装饰器类名前面加上 @ 符号,后跟被装饰的函数。它需要实现一个 __init__
方法来接收被装饰的函数,以及一个 __call__
方法来替代函数的行为。对于熟悉装饰器的同学来说,类装饰器的__call__
其实就是装饰器的内层函数。
类装饰器常用于在运行时动态修改函数的行为,例如记录函数调用日志,缓存函数返回值,检查函数参数等。
2 类装饰器建立流程
-
定义一个类,该类需要实现一个 init 方法来接收被装饰的函数。
-
定义一个 call 方法来替代被装饰函数的行为。
-
使用 @ 符号将类装饰器类名标记在被装饰函数前面。
与装饰器类似,类装饰器的使用方法如下:
@decorator_class
def decorated_function():
pass
其中 decorator_class 是一个类,它实现了 __init__
和 __call__
方法, decorated_function
是被装饰的函数。
在使用类装饰器时需要注意的是,类装饰器的实例是在导入模块时创建的,而不是在调用函数时创建的。这意味着如果类装饰器类中定义了状态,那么所有使用该装饰器的函数将共享该状态。
3 类装饰器示例代码
为了更好的了解类装饰器,我们来看一段包含类装饰器的完整代码。在这个类装饰器中,实现了生成被装饰函数的使用日志这一常用功能。
"""
类装饰器实现了一个 __call__ 方法来记录函数的使用日志。这个装饰器记录函数运行的开始时间和结束时间,并打印出来。
使用类装饰器时,需要注意的是,类装饰器需要实现一个 __call__ 方法来替代函数的行为。此外,类装饰器需要实现一个 __init__ 方法来接收被装饰的函数。
"""
import datetime
import time
class LoggingDecorator:
def __init__(self, func):
self.func = func
def __call__(self, *args, **kwargs):
start_time = datetime.datetime.now()
result = self.func(*args, **kwargs)
end_time = datetime.datetime.now()
print(f'{self.func.__name__} ran from {start_time} to {end_time}')
return result
@LoggingDecorator
def my_function():
time.sleep(1)
print('Hello, World!')
my_function()
这段函数的输出如下:
虽然这里我们定义的my_function()
函数并没有传参进去,但是类装饰器的__call__
方法仍然传入了(*args, **kwargs),这使得我们这个装饰器同样可以作用于带参数的函数。
如果希望将日志保存下来,可以将print函数部分进行替换成如下内容:
with open('log.txt','a',encoding='utf-8') as f:
f.write(f'{self.func.__name__} ran from {start_time} to {end_time}')
这样,就可以将每次的输出保存在一个文本文件中。
4 原理解释
在定义装饰器的时,等同于以后在执行函数时,将执行
LoggingDecorator(my_function())
即将函数传入了装饰器类。
在之后执行my_function()方法(或理解为类方法)时,__call__
方法便会被自动执行,因此类装饰器发挥了作用。