You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用装饰器类装饰方法时,如何让返回的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。

解决方案的核心原理

  1. 描述符__get__方法:

    • 当通过实例(如foo.bar)访问被装饰的方法时,Python会调用Wrapper.__get__(foo, Foo),我们返回一个新的Wrapper实例,把foo存入__bound_instance属性。
    • 当通过类(如Foo.bar)访问时,返回未绑定的原Wrapper实例,保持和普通未绑定方法一致的调用方式。
  2. 绑定后的__call__逻辑:

    • 调用绑定了实例的Wrapper对象时,__call__会自动把__bound_instance作为第一个参数传给原函数,模拟Python原生的方法绑定行为。
    • 未绑定的Wrapper实例则直接传递调用时的参数,兼容类调用的场景。

这样既解决了self绑定问题,又严格满足了你“访问属性时返回Wrapper类型”的要求,不会返回functools.partial或method对象。

内容的提问来源于stack exchange,提问作者Mark G

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.14 07:31:39