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

如何让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中的值

问题原因

  1. 赋值操作未被拦截:Python对实例属性赋值时,默认直接将属性存入实例__dict__,不会触发__getattr__,原Wrapper未实现__setattr__来拦截转发赋值操作。
  2. 属性查找优先级:执行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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.16 01:47:30