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

用可调用类与普通函数装饰类方法为何表现不同?

问题:可调用类装饰器装饰类方法时实例调用报错的原因?

尝试为类方法添加装饰器时,用普通函数装饰一切正常,但用实现__call__的可调用类装饰时,类级调用正常,实例级调用抛出TypeError提示缺少self参数。最小复现代码如下:

import random
class C():
    """A simple class"""
    def __init__(self):
        self.x = random.random()

    def report(self):
        return 'The value is: '+str(self.x)
class F():
    """Simulate a function"""
    def __init__(self, f):
        self.f = f
    def __call__(self, *args, **kwargs):
        return self.f(*args, **kwargs)

def decoratef(f):
    def f1(*args, **kwargs):
        return f(*args, **kwargs)
    return f1
def decorateF(f):
    def f1(*args, **kwargs):
        return f(*args, **kwargs)
    return F(f1) # This F is the only difference.
report0 = C.report

C.report = decoratef(report0)
print('Test_f0:', C.report(C())) # Works.
print('Test_f1:', C().report()) # Works.

C.report = decorateF(report0)
print('Test_F0:', C.report(C())) # Works.
print('Test_F1:', C().report()) #TypeError: "C.report() missing 1 required positional argument: 'self'"

核心原因:Python方法绑定依赖描述符协议

Python中,类里定义的普通函数属于非数据描述符,它们自带实现了__get__方法。当实例访问类的函数属性时,__get__会自动把实例作为第一个参数(即self)绑定到函数上,生成绑定方法。比如C().report会变成绑定方法,调用时自动传入实例本身,无需手动传self。

而你定义的可调用类F只实现了__call__,没有实现描述符协议的__get__方法。当实例访问C().report时,不会触发自动绑定逻辑,返回的就是F的实例本身。调用这个实例时,__call__直接调用内部的f1,但f1原本是需要接收self参数的类方法,此时没有自动传入实例,自然会报错缺少self。

类级调用C.report(C())能正常工作,是因为你手动传入了实例作为第一个参数,相当于绕过了自动绑定步骤,和直接调用普通函数的逻辑一致。

如果要让可调用类装饰器支持实例方法绑定,只需给F添加__get__方法,示例如下:

class F():
    """Simulate a function"""
    def __init__(self, f):
        self.f = f
    def __call__(self, *args, **kwargs):
        return self.f(*args, **kwargs)
    def __get__(self, instance, owner):
        # 实现绑定逻辑,返回绑定方法
        return lambda *args, **kwargs: self.f(instance, *args, **kwargs)

这样C().report会通过__get__生成绑定了实例的方法,调用时就会自动传入self参数。

内容的提问来源于stack exchange,提问作者Kevin Kostlan

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.26 06:43:15