Python自定义类作为类方法装饰器复用代码触发TypeError如何解决
问题根因
- 两种装饰用法的逻辑差异:
- 无参数
@Decorator:类的方法属性直接是Decorator实例,该实例实现了__get__描述符协议,访问时会自动绑定类实例作为第一个参数,所以运行正常。 - 带参数
@Decorator():先实例化Decorator对象,再调用该对象的__call__方法传入被装饰函数,返回值会作为类的方法属性。
- 报错的核心原因:
修改后__call__直接返回self.call,这是已经绑定到Decorator实例的绑定方法。Python的方法绑定规则是:只有未绑定的普通函数作为类属性存在时,通过实例访问才会自动把实例作为第一个参数传入;已经绑定的方法不会触发该逻辑,所以调用hello_world.print("Max Musterman")时,实例没有被传入call方法,call拿到的第一个参数是字符串"Max Musterman",传给原方法时缺少name参数,触发报错。
你观察到的inspect.signature返回一致,是因为inspect会自动隐藏绑定方法的self参数,本质上两个可调用对象的类型完全不同,作为类方法属性的行为也完全不同。
解决方案
不需要直接返回self.call,只要在wrapper中调用self.call即可复用逻辑,不存在重复代码问题,还可以用functools.wraps保持原函数的元信息:
from functools import partial, wraps class Decorator: def __init__(self, func = None, *args): self.uses_init = func is None self.func = func self.instance = None self.args = args def __call__(self, *args, **kwargs): self.func = args[0] @wraps(self.func) def wrapper(cls, *args, **kwargs): return self.call(cls, *args, **kwargs) return wrapper def call(self, cls, *args, **kwargs): print('before') parsed_func = self.func(cls, *args, **kwargs) print('after') return parsed_func def __get__(self, instance, owner): # This is only used when uses_init == False return partial(self.call, instance)
该实现完全兼容你原来的测试用例,所有核心逻辑都统一在call方法中维护。
内容的提问来源于stack exchange,提问作者PythonF
相关产品推荐
相关产品推荐

