类装饰器必须实现__call__方法才能被调用;其__init__接收配置参数,__call__接收被装饰函数并返回wrapper;支持状态保存、带参语法及实例方法装饰需注意绑定问题。

类装饰器必须实现 __call__ 才能被调用
Python 的装饰器本质是可调用对象,函数装饰器靠闭包返回函数,而类装饰器必须让实例可调用——也就是实现 __call__ 方法。没写这个方法,直接加 @MyDecorator 会报 TypeError: 'MyDecorator' object is not callable。
常见错误是只写了 __init__ 就以为完事了,结果装饰后函数根本执行不了。正确结构是:
class LogDecorator:
def __init__(self, level="INFO"):
self.level = level
<pre class="brush:php;toolbar:false;">def __call__(self, func):
def wrapper(*args, **kwargs):
print(f"[{self.level}] Calling {func.__name__}")
result = func(*args, **kwargs)
print(f"[{self.level}] Done {func.__name__}")
return result
return wrapper
注意:__call__ 接收的是被装饰的 func,不是参数;参数在内部 wrapper 中接收。
带参数的类装饰器要多嵌套一层 __call__
如果想支持 @LogDecorator(level="DEBUG") 这种带参写法,类本身不能直接接收业务参数——因为装饰语法 @xxx 调用时传的是函数,不是字符串或数字。必须用“类实例作为装饰器工厂”的模式:
- 第一层:类初始化(
__init__)接收配置参数,比如level、timeout - 第二层:实例的
__call__接收被装饰的func,返回真正的包装函数
漏掉这一层,就会出现 TypeError: __call__() missing 1 required positional argument: 'func'。示例中若把 level 放到 __call__ 参数里,就无法通过 @LogDecorator(level="DEBUG") 语法传入。
类装饰器中保存状态比函数装饰器更直观
需要跨调用记录次数、缓存结果、统计耗时等场景,类装饰器天然适合——状态存在实例属性上,不用像函数装饰器那样依赖闭包变量或 nonlocal。
比如实现一个简单调用计数器:
class CountCalls:
def __init__(self):
self.count = 0
<pre class="brush:php;toolbar:false;">def __call__(self, func):
def wrapper(*args, **kwargs):
self.count += 1
print(f"{func.__name__} called {self.count} times")
return func(*args, **kwargs)
return wrapper使用
@CountCalls() def hello(): pass hello() # hello called 1 times hello() # hello called 2 times
注意:每次 @CountCalls() 都创建新实例,所以每个被装饰函数有独立计数;如果想全局共享,得把计数器提到类变量或外部模块级。
注入业务逻辑时小心 __get__ 和描述符协议
当把类装饰器用在实例方法上(比如 class Service: 里的 def fetch(self):),会遇到绑定问题:wrapper 函数不会自动接收 self,导致 TypeError: wrapper() missing 1 required positional argument: 'self'。
根本原因是方法在访问时触发描述符协议,而普通 wrapper 不是描述符。解决办法是在 wrapper 内部显式处理绑定逻辑,或改用 functools.wraps + 手动适配:
from functools import wraps
<p>class AuthDecorator:
def <strong>init</strong>(self, role="user"):
self.role = role</p><pre class="brush:php;toolbar:false;">def __call__(self, func):
@wraps(func)
def wrapper(instance, *args, **kwargs):
if not hasattr(instance, "user_role") or instance.user_role != self.role:
raise PermissionError("Access denied")
return func(instance, *args, **kwargs)
return wrapper
这里 wrapper 显式声明第一个参数为 instance,并假设被装饰方法是实例方法。如果是静态方法或类方法,还得额外判断 func 类型——这点容易被忽略,一跑就报错。
类装饰器看着简洁,但一旦涉及方法绑定、继承链、多重装饰,self / cls 的传递顺序和存在性就变得微妙。动手前先确认目标是函数、实例方法,还是类方法。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











