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

为何innerclass装饰器实现的Descriptor __set__未触发?如何修复?

问题原因分析
  1. 实例属性直接覆盖:foo.inner_value是Outer实例的实例属性,执行foo.inner_value = 42时,Python会直接替换该实例属性的引用,完全不会触发Inner类中的__set__方法——这个方法只是Inner实例的普通方法,和属性赋值逻辑无关。
  2. 描述符协议误解:描述符的__set__方法需要定义在类层面的描述符对象上(作为类属性存在),才能在赋值类属性/实例属性时被触发。你当前的__set__是Inner类的实例方法,不属于描述符协议的范畴,自然不会被Python的属性访问机制调用。
  3. 装饰器功能局限:你的innerclass装饰器只实现了描述符的__get__方法,仅解决了创建Inner实例时自动传入外部类实例的问题,没有处理属性赋值的拦截逻辑。
修复方案

根据需求,提供两种可行的修复思路:

思路一:用@property包装实例属性

通过@property为inner_value添加自定义的getter和setter,拦截赋值操作并调用Inner实例的更新逻辑:

from functools import partial

class innerclass:
    def __init__(self, cls):
        self.cls = cls

    def __get__(self, obj, obj_type=None):
        if obj is None:
            return self.cls
        return partial(self.cls, obj)

class Outer:
    def __init__(self):
        # 用私有变量存储Inner实例
        self._inner_value = self.Inner('foo')

    @innerclass
    class Inner:
        def __init__(self, outer_instance, value):
            self.outer = outer_instance
            self.value = value

        # 定义专门的更新方法,替代原__set__
        def update_value(self, value):
            print('Setter invoked')
            self.value = value

    @property
    def inner_value(self):
        return self._inner_value

    @inner_value.setter
    def inner_value(self, value):
        # 拦截赋值,调用Inner实例的更新方法
        self._inner_value.update_value(value)

测试验证

foo = Outer()
print(type(foo.inner_value))  # <class '__main__.Outer.Inner'>
foo.inner_value = 42  # 输出: Setter invoked
print(foo.inner_value.value)  # 42
print(type(foo.inner_value))  # <class '__main__.Outer.Inner'>

思路二:扩展innerclass为完整描述符

修改innerclass装饰器,实现描述符的__set__方法,让Inner作为类层面的描述符管理每个外部实例的Inner对象:

from functools import partial

class innerclass:
    def __init__(self, cls):
        self.cls = cls
        # 存储每个外部实例对应的Inner实例
        self._instance_map = {}

    def __get__(self, obj, obj_type=None):
        if obj is None:
            return self.cls
        # 不存在则创建实例
        if obj not in self._instance_map:
            # 若需要初始化参数,可调整逻辑或在Outer中处理
            self._instance_map[obj] = self.cls(obj)
        return self._instance_map[obj]

    def __set__(self, obj, value):
        # 处理赋值逻辑:存在实例则更新,不存在则创建
        if obj in self._instance_map:
            print('Setter invoked')
            self._instance_map[obj].value = value
        else:
            self._instance_map[obj] = self.cls(obj, value)

class Outer:
    @innerclass
    class Inner:
        def __init__(self, outer_instance, value=None):
            self.outer = outer_instance
            self.value = value

# 测试验证
foo = Outer()
# 初始化赋值
foo.Inner = 'foo'
print(type(foo.Inner))  # <class '__main__.Outer.Inner'>
foo.Inner = 42  # 输出: Setter invoked
print(foo.Inner.value)  # 42
print(type(foo.Inner))  # <class '__main__.Outer.Inner'>

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.21 04:12:48