Python 使用 `__call__` 实现装饰器

python 装饰器实现

__call__

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
    def __call__(self, fn: AnyCallable) -> HTTPRouteHandler:
        """Replace a function with itself."""
        if not is_async_callable(fn):
            if self.sync_to_thread is None:
                warn_implicit_sync_to_thread(fn, stacklevel=3)
        elif self.sync_to_thread is not None:
            warn_sync_to_thread_with_async_callable(fn, stacklevel=3)

        super().__call__(fn)
        return self
1
2
3
4
def __call__(self, fn: AsyncAnyCallable) -> Self:
        """Replace a function with itself."""
        self._fn = fn
        return self

在 Python 中,使用类创建装饰器可以通过实现 __call__ 方法并结合 __init__ 方法来完成。以下是常见的几种方式:


1. 基础装饰器类

定义一个类,通过 __init__ 接收被装饰的函数,__call__ 实现装饰逻辑。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
class SimpleDecorator:
    def __init__(self, func):
        self.func = func  # 保存被装饰的函数

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

@SimpleDecorator
def greet(name):
    print(f"Hello, {name}!")

greet("Alice")
# 输出:
# Before function execution
# Hello, Alice!
# After function execution

Python 装饰器原理解析

在这段代码中,能够传递func参数是因为 Python 装饰器的工作原理。当你使用@语法装饰一个函数时,Python 实际上执行了一个特殊的操作流程:

装饰器执行过程

  1. 当 Python 解释器执行到@SimpleDecorator装饰器时,它会先定义greet函数
  2. 然后将greet函数作为参数传递给SimpleDecorator构造函数
  3. 最终用SimpleDecorator的实例替换原始的greet函数

实际上,这个语法糖:

1
2
3
@SimpleDecorator
def greet(name):
    print(f"Hello, {name}!")

等同于:

1
2
3
4
def greet(name):
    print(f"Hello, {name}!")

greet = SimpleDecorator(greet)  # 将函数作为参数传递给装饰器

调用过程

当执行greet("Alice")时:

  1. 实际上是在调用SimpleDecorator的实例
  2. 由于该类实现了__call__方法,它的实例是可调用的对象
  3. 调用时执行了__call__方法,该方法中调用了保存的原始函数self.func

2. 带参数的装饰器类

如果需要装饰器接受额外参数,需分两层处理:外层类接收参数,内层处理函数。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
class ParametrizedDecorator:
    def __init__(self, prefix):
        self.prefix = prefix  # 保存装饰器参数

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

@ParametrizedDecorator("DEBUG")
def add(a, b):
    return a + b

print(add(2, 3))
# 输出:
# DEBUG: Before function execution
# DEBUG: After function execution
# 5

3. 支持函数和方法的装饰器类

若装饰器需要同时支持函数和类方法,需确保正确处理 self 参数:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
class UniversalDecorator:
    def __init__(self, func):
        self.func = func

    def __call__(self, *args, **kwargs):
        print("Decorator logic here")
        return self.func(*args, **kwargs)

    # 解决方法绑定问题(可选)
    def __get__(self, instance, owner):
        from functools import partial
        return partial(self.__call__, instance)

class MyClass:
    @UniversalDecorator
    def my_method(self):
        print("Method called")

obj = MyClass()
obj.my_method()
# 输出:
# Decorator logic here
# Method called

4. 装饰类本身的装饰器类

通过修改或扩展类的行为,装饰器可以作用于类:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
class ClassDecorator:
    def __init__(self, cls):
        self.cls = cls  # 保存被装饰的类

    def __call__(self, *args, **kwargs):
        # 修改实例化行为
        instance = self.cls(*args, **kwargs)
        # 添加新方法
        instance.new_method = lambda: print("New method added!")
        return instance

@ClassDecorator
class MyClass:
    def __init__(self, value):
        self.value = value

obj = MyClass(10)
obj.new_method()  # 输出: New method added!

5. 可维护状态的装饰器类

利用类的实例属性保存状态(如调用次数):

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
class StatefulDecorator:
    def __init__(self, func):
        self.func = func
        self.count = 0  # 记录调用次数

    def __call__(self, *args, **kwargs):
        self.count += 1
        print(f"Function called {self.count} times")
        return self.func(*args, **kwargs)

@StatefulDecorator
def say_hello():
    print("Hello!")

say_hello()  # 输出: Function called 1 times → Hello!
say_hello()  # 输出: Function called 2 times → Hello!

6. 继承实现装饰器

通过继承基类扩展装饰器功能:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
class BaseDecorator:
    def __init__(self, func):
        self.func = func

    def __call__(self, *args, **kwargs):
        return self.func(*args, **kwargs)

class TimingDecorator(BaseDecorator):
    import time
    def __call__(self, *args, **kwargs):
        start = self.time.time()
        result = super().__call__(*args, **kwargs)
        end = self.time.time()
        print(f"Function took {end - start:.2f} seconds")
        return result

@TimingDecorator
def slow_function():
    import time
    time.sleep(1)

slow_function()  # 输出: Function took 1.00 seconds
Licensed under CC BY-NC-SA 4.0
comments powered by Disqus