为何innerclass装饰器实现的Descriptor __set__未触发?如何修复?
问题原因分析
- 实例属性直接覆盖:
foo.inner_value是Outer实例的实例属性,执行foo.inner_value = 42时,Python会直接替换该实例属性的引用,完全不会触发Inner类中的__set__方法——这个方法只是Inner实例的普通方法,和属性赋值逻辑无关。 - 描述符协议误解:描述符的
__set__方法需要定义在类层面的描述符对象上(作为类属性存在),才能在赋值类属性/实例属性时被触发。你当前的__set__是Inner类的实例方法,不属于描述符协议的范畴,自然不会被Python的属性访问机制调用。 - 装饰器功能局限:你的
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
相关产品推荐
相关产品推荐

