一、基本原理

使用类实现装饰器的基本原理:

  • 定义一个类,__init__ 方法用于初始化装饰器的状态, __call__ 方法用于定义在调用被装饰函数时所执行的逻辑。
  • 通过类创建一个可调用对象(类的实例),该对象在被调用时执行 __call__ 方法中的装饰逻辑。

二、Demo 示例

示例 1:简单的类装饰器

import functools


class SimpleDecorator:
    def __init__(self, func):
        self.func = func
        functools.wraps(func)(self)

    def __call__(self, *args, **kwargs):
        print("Before function execution")
        result = self.func(*args, **kwargs)
        print("After function execution")
        return result


@SimpleDecorator
def my_function():
    """This is the original function."""
    print("Function is being executed")


my_function()
print(my_function.__name__)  # 输出 "my_function"

输出结果:

Before function execution
Function is being executed
After function execution
my_function

在这个例子中,SimpleDecorator 类实现了一个简单的装饰器,它在被装饰函数执行前后打印额外的信息。

  • 使用 @SimpleDecorator 装饰函数 my_function 时,相当于执行了: my_function = SimpleDecorator(my_function)
  • my_functionSimpleDecorator 类的实例,由于在类中定义了 __call__ 方法,因此 my_function 是一个可调用对象,可以像函数一样调用。
  • 执行 my_function() 时,调用 __call__ 方法中定义的装饰逻辑,实现装饰器的效果。

functools.wraps(func)(self) 的理解:

  • functools.wraps(func)(self) 等价于两个步骤:
    • 创建装饰器:decorator = functools.wraps(func)
    • 使用装饰器:decorator(self)
  • decorator = functools.wraps(func) 的作用是将函数 func 的元信息(如名称、文档字符串等)复制给被装饰函数(在这里是 __call__ 方法)。
  • decorator(self) 的作用将 __call__ 方法的实例对象传递给 decorator 装饰器,这个过程实际上是在调用 decorator 装饰器 ,并将 __call__ 方法作为参数传递进去。

示例 2:带参数的类装饰器

import functools


class ParametrizedDecorator:
    def __init__(self, param):
        self.param = param

    def __call__(self, func):
        @functools.wraps(func)
        def wrapper(*args, **kwargs):
            print(f"Decorator parameter: {self.param}")
            for i in range(self.param):
                func(*args, **kwargs)

        return wrapper


@ParametrizedDecorator(param=3)
def greet(name):
    print(f"Hello, {name}!")


greet("Alice")
print(greet.__name__)  # 输出 "greet"

输出结果:

Decorator parameter: 3
Hello, Alice!
Hello, Alice!
Hello, Alice!
greet

这个例子中,ParametrizedDecorator 接受一个参数,并在装饰函数执行前打印该参数。

  • 使用 @ParametrizedDecorator(param=3) 装饰 greet 函数,相当于:greet = ParametrizedDecorator(param=3)(greet)
  • 在执行 ParametrizedDecorator(param=3)(greet) 时,调用了 __call__ 方法,返回一个新的 wrapper 函数,此时 greet 方法相当于
    wrapper 函数的引用。
  • 执行 greet("Alice") 等同于执行 wrapper("Alice"),从而实现了装饰器的效果。

三、要点小结

  • 在类中定义 __call__ 方法,可以将类的实例变为可调用对象,使其可以像函数一样被调用。
  • 当对象被调用时,会执行 __call__ 方法中的逻辑。可利用这一特性通过定义 __call__ 方法中的逻辑实现装饰效果。
  • 可以通过在 __init__ 方法中接受参数来实现带参数的类装饰器,这使得类装饰器更加灵活,可以根据传递的参数定制装饰行为。
01-16 15:04