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

Python 3.11.2中使用描述符协议绑定类方法报错问题

Python 3.11中跨类绑定方法失效的问题解决

问题场景

为了在新增API语法糖时保持向后兼容,通过描述符协议将Decorator类的方法绑定到SubscriptableFunctionWrapper实例,同时锁定Decorator的self值。这套逻辑在Python 3.7~3.10中正常运行,但在Python 3.11中失效。

核心需求的简化代码如下:

包装函数类

class SubscriptableFunctionWrapper:

    def __init__(self, fn, decorator_name_variant_method):
        self.fn = fn
        self.name_variant = decorator_name_variant_method.__get__(self)

    def __getitem__(self, keys):
        return 42

    def __call__(self, *args, **kwargs):
        return self.fn(*args, **kwargs)

装饰器类

class Decorator:

    def __init__(self, name = "", variant = "default"):
        self.name = name
        self.variant = variant

    def __call__(self, fn):
        if self.name == "":
            self.name = fn.__name__
        return SubscriptableFunctionWrapper(fn, self.name_variant)

    def name_variant(self):
        return (self.name, self.variant)

测试代码

@Decorator()
def adds_two(a):
    return a + 2

assert adds_two(2) == 4 # 调用功能正常
assert adds_two["keys"] == 42 # 下标访问正常
assert adds_two.name_variant() == ("adds_two", "default") # 绑定方法调用失效(3.11中报错)

失效原因

Python 3.11对绑定方法(instancemethod)的内部校验逻辑做了更新:当你通过method.__get__(instance)将一个类的方法绑定到另一个类的实例时,调用该绑定方法会检查调用者的类型是否与方法定义的类匹配。

在你的代码中,name_variant是Decorator类的实例方法,却被绑定到了SubscriptableFunctionWrapper实例。3.11中调用adds_two.name_variant()时,Python会验证调用者(adds_two)是否是Decorator的实例,显然不满足,因此抛出类型错误。

修复方案

以下三种方案均可解决问题,可根据实际场景选择:

方案1:手动包装方法固定self

直接创建一个包装函数,把Decorator实例作为方法的第一个参数传入,绕过描述符的绑定校验:

class SubscriptableFunctionWrapper:

    def __init__(self, fn, decorator_instance):
        self.fn = fn
        # 手动包装,固定Decorator实例作为方法的self
        def bound_name_variant():
            return decorator_instance.name_variant()
        self.name_variant = bound_name_variant

    def __getitem__(self, keys):
        return 42

    def __call__(self, *args, **kwargs):
        return self.fn(*args, **kwargs)

# 对应修改Decorator的__call__方法
class Decorator:
    # ... 其他代码不变 ...
    def __call__(self, fn):
        if self.name == "":
            self.name = fn.__name__
        return SubscriptableFunctionWrapper(fn, self)

方案2:用functools.partial绑定参数

借助functools.partial固定方法的第一个参数(即Decorator的实例),更简洁:

from functools import partial

class SubscriptableFunctionWrapper:

    def __init__(self, fn, decorator_instance):
        self.fn = fn
        self.name_variant = partial(decorator_instance.name_variant)

    def __getitem__(self, keys):
        return 42

    def __call__(self, *args, **kwargs):
        return self.fn(*args, **kwargs)

# Decorator的__call__修改同方案1

方案3:动态代理所有Decorator方法

如果需要绑定多个Decorator的方法,用__getattr__实现动态代理更灵活,无需逐个绑定:

class SubscriptableFunctionWrapper:

    def __init__(self, fn, decorator_instance):
        self.fn = fn
        self._decorator = decorator_instance

    def __getattr__(self, name):
        # 访问不存在的属性时,从Decorator实例获取并执行
        attr = getattr(self._decorator, name)
        if callable(attr):
            return lambda *args, **kwargs: attr(*args, **kwargs)
        return attr

    def __getitem__(self, keys):
        return 42

    def __call__(self, *args, **kwargs):
        return self.fn(*args, **kwargs)

# Decorator的__call__修改同方案1

验证

修改后的代码在Python 3.11及之前版本均可正常运行,满足所有需求:调用、下标访问、绑定方法返回正确结果。

内容的提问来源于stack exchange,提问作者Erik Eppel

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.25 08:47:48