使用装饰器类装饰方法时,如何让返回的Callable绑定到self?
解决装饰器类绑定实例方法时的self传递问题
要让你的Wrapper类既能正确绑定实例方法的self,又能保证访问属性时返回的是Wrapper类型,关键是让Wrapper实现描述符协议的__get__方法——这正是Python处理方法绑定的底层机制。
完整解决方案代码
import functools class Wrapper: def __init__(self, func, bound_instance=None) -> None: super().__init__() self.__func = func self.__bound_instance = bound_instance # 存储绑定的实例对象 functools.update_wrapper(self, func) # 保留原函数的元信息(如名称、文档字符串) def __call__(self, *args, **kwargs): # 根据是否绑定实例,决定是否自动传入self if self.__bound_instance is not None: result = self.__func(self.__bound_instance, *args, **kwargs) else: result = self.__func(*args, **kwargs) print("PreBar") print("PostBar") return result def __get__(self, instance, owner): # 描述符方法:处理实例/类属性的访问逻辑 if instance is None: # 通过类访问时,返回未绑定的原Wrapper实例 return self # 通过实例访问时,返回绑定了该实例的新Wrapper对象 return Wrapper(self.__func, bound_instance=instance) def MyDecorator(func): return Wrapper(func) class Foo: baz = "Baz" @MyDecorator def bar(self): print(self.baz) return self.baz if __name__ == '__main__': foo = Foo() print(f"Type of foo.bar: {type(foo.bar)}") # 输出 <class '__main__.Wrapper'> print(f"Result of foo.bar(): {foo.bar()}") print(f"Type of Foo.bar: {type(Foo.bar)}") # 输出 <class '__main__.Wrapper'> print(f"Result of Foo.bar(foo): {Foo.bar(foo)}")
为什么原代码会报错?
你的Wrapper类本质是一个普通可调用对象,没有实现描述符协议。当它被用作类的方法属性时:
- 调用
foo.bar()时,Python不会自动把foo作为self传递给Wrapper.__call__——只有遵循描述符协议的对象(比如原生函数、classmethod等)才会触发自动绑定逻辑。 - 最终导致原函数
bar缺少必填的self参数,抛出TypeError。
解决方案的核心原理
描述符
__get__方法:- 当通过实例(如
foo.bar)访问被装饰的方法时,Python会调用Wrapper.__get__(foo, Foo),我们返回一个新的Wrapper实例,把foo存入__bound_instance属性。 - 当通过类(如
Foo.bar)访问时,返回未绑定的原Wrapper实例,保持和普通未绑定方法一致的调用方式。
- 当通过实例(如
绑定后的
__call__逻辑:- 调用绑定了实例的
Wrapper对象时,__call__会自动把__bound_instance作为第一个参数传给原函数,模拟Python原生的方法绑定行为。 - 未绑定的
Wrapper实例则直接传递调用时的参数,兼容类调用的场景。
- 调用绑定了实例的
这样既解决了self绑定问题,又严格满足了你“访问属性时返回Wrapper类型”的要求,不会返回functools.partial或method对象。
内容的提问来源于stack exchange,提问作者Mark G
相关产品推荐
相关产品推荐

