
当用类实现装饰器包装器时,因未实现描述符协议,无法自动绑定实例方法的`self`参数,导致调用时需手动传入对象;而函数式装饰器天然支持该机制,是更简洁、可靠的选择。
在 Python 中,实例方法的自动 self 绑定并非语言魔法,而是基于描述符协议(Descriptor Protocol) 的标准行为。当访问一个类属性(如 obj.method)时,若该属性对象实现了 __get__ 方法,Python 会自动调用它,并传入实例(obj)和所属类(type(obj))。内置函数对象(function)正是通过这一机制,在被访问为实例属性时返回一个绑定方法(bound method),从而将 self 隐式注入后续调用。
而问题中的 broken_decorator 返回的是一个普通类实例(Wrapper()),尽管它定义了 __call__,却未实现 __get__,因此不构成非数据描述符(non-data descriptor)。结果就是:three.plus_ 直接返回该 Wrapper 实例本身,而非绑定后的可调用对象;调用 three.plus_(4) 实际等价于 wrapper.__call__(4) —— 此时 self 参数缺失,Python 不会自动补上 three,而是将第一个显式参数 4 错误地当作 my_class_self,造成逻辑混乱(如示例中需传入 MyClass(4) 才“凑效”)。
✅ 正确做法:优先使用闭包函数作为装饰器包装器(如 @decorator):
def decorator(func):
def wrapper(self, y): # self 自动由绑定机制传入
print(f"Before calling {func.__name__}")
result = func(self, y)
print("After call")
return result
return wrapper⚠️ 若必须用类封装状态(如计数器、缓存等),应显式实现描述符协议:
立即学习“Python免费学习笔记(深入)”;
class StatefulDecorator:
def __init__(self, func):
self.func = func
self.call_count = 0
def __call__(self, *args, **kwargs):
self.call_count += 1
return self.func(*args, **kwargs)
def __get__(self, obj, objtype=None):
# 当通过实例访问时,返回绑定的代理对象
if obj is None:
return self # 类访问,返回装饰器自身
# 返回一个能记住 obj 和 self.func 的绑定对象
return lambda *args, **kwargs: self.func(obj, *args, **kwargs)
# 使用示例(此时 three.plus_ 会正确绑定 self=three)
@StatefulDecorator
def plus_(self, y):
print(f"{self.x} + {y} = {self.x + y}")? 总结:
- 函数式装饰器开箱即用,天然支持方法绑定,性能优、代码简;
- 类装饰器需手动实现 __get__ 才能模拟绑定行为,逻辑复杂且易出错;
- 除非有强需求(如需跨实例共享状态、动态配置或类型提示增强),否则避免为装饰器包装器使用无描述符支持的类;
- 调试时可检查 hasattr(decorated_method, '__func__') —— 若为 True,说明已成功绑定;若调用时报 TypeError: missing 1 required positional argument: 'self',大概率是包装器未遵循描述符协议。










