如何让Python的getattr()尊重类的property属性?
如何让Wrapper类正确代理被包装对象的property属性
问题场景
现有一个封装了property的Test类,通过value属性可直接调用对应的getter和setter:
class Test: def __init__(self): self.__value = None def set_value(self, value): print(f"set_value(): value={value}") self.__value = value def get_value(self): print(f"get_value()") return self.__value value = property(get_value, set_value)
为了给Test对象的所有访问操作添加额外逻辑,实现了Wrapper类:
class Wrapper: def __init__(self, wrapped): self.wrapped = wrapped def __getattr__(self, attr): print(f"Wrapper::__getattr__(): {attr}") return getattr(self.wrapped, attr)
但使用时发现:直接调用set_value/get_value方法正常,而通过w.value赋值时,会直接给Wrapper实例添加value属性,无法触发Test类的property setter:
t = Test() w = Wrapper(t) w.set_value(123) print(f"Value: {w.get_value()}") w.value = 234 # 直接给Wrapper实例赋值,不调用Test的set_value print(f"Value: {w.value}") # 输出Wrapper自身的value,而非Test中的值
问题原因
- 赋值操作未被拦截:Python对实例属性赋值时,默认直接将属性存入实例
__dict__,不会触发__getattr__,原Wrapper未实现__setattr__来拦截转发赋值操作。 - 属性查找优先级:执行
w.value = 234后,Wrapper实例自身已有value属性,后续获取w.value时会直接返回该值,不再通过__getattr__访问被包装对象的property。
解决方案
实现__setattr__方法,拦截所有属性赋值操作,区分Wrapper自身属性和需转发的属性,同时识别被包装对象类中的property并正确转发:
class Wrapper: def __init__(self, wrapped): # 使用super().__setattr__避免触发自定义的__setattr__ super().__setattr__('wrapped', wrapped) def __getattr__(self, attr): print(f"Wrapper::__getattr__(): {attr}") return getattr(self.wrapped, attr) def __setattr__(self, attr, value): # 处理Wrapper自身的属性(如wrapped) if attr == 'wrapped': super().__setattr__(attr, value) else: wrapped_obj = self.wrapped wrapped_cls = type(wrapped_obj) # 检查被包装类是否存在该属性且为property类型 if hasattr(wrapped_cls, attr) and isinstance(getattr(wrapped_cls, attr), property): print(f"Wrapper::__setattr__(): forwarding property {attr} = {value}") setattr(wrapped_obj, attr, value) else: # 转发普通属性赋值 print(f"Wrapper::__setattr__(): forwarding attribute {attr} = {value}") setattr(wrapped_obj, attr, value)
测试验证
运行以下代码:
t = Test() w = Wrapper(t) w.set_value(123) print(f"Value: {w.get_value()}") w.value = 234 print(f"Value: {w.value}")
输出结果:
set_value(): value=123 Wrapper::__getattr__(): get_value get_value() Value: 123 Wrapper::__setattr__(): forwarding property value = 234 set_value(): value=234 Wrapper::__getattr__(): value get_value() Value: 234
可见w.value = 234正确触发了Test类的set_value方法,w.value也正确获取到Test中的值。
优势保留
该方案仍保留原Wrapper的核心优势:
- 原
Test类无需任何修改,可独立使用; Test类新增方法或属性时,无需改动Wrapper,自动完成转发。
内容的提问来源于stack exchange,提问作者Oleksandr Masliuchenko
相关产品推荐
相关产品推荐

