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

工厂方法中用setattr设置属性,为何实例属性带前置下划线?

问题原因与解决方案

首先澄清一个点:你的setattr(obj, 'age', value)其实并没有直接设置_age属性——它正常触发了age的setter方法,self._age被正确赋值了,这部分逻辑是没问题的。

你看到输出{"_age": "64"}的核心问题出在__str__方法的实现上:

你用json.dumps(self, default=lambda o: o.__dict__)直接序列化了实例的__dict__,而__dict__里存储的是实例的直接属性(也就是你在__init__里定义的_age)。而age是类层面定义的property,它不属于实例的__dict__,所以序列化时自然不会被包含进去,只会显示底层的_age。

另外,你的__setattr__方法是安全的——它直接调用object.__setattr__,没有覆盖property的默认处理逻辑,所以setattr操作age时确实会走你定义的setter,这点不用改。

解决方法

想要输出{"age": "64"},你需要修改__str__方法,让它序列化property对应的公开属性名,而不是底层的私有变量:

方法1:自动收集所有property属性

如果你的类有很多property,不想手动列出来,可以自动遍历类的属性,收集所有property的名称和对应值:

import json

class MyClass(object):
    # ... 其他代码不变 ...

    def __str__(self):
        serialized = {}
        # 遍历类的所有属性,筛选出property类型的
        for attr_name in dir(self):
            class_attr = getattr(type(self), attr_name, None)
            if isinstance(class_attr, property):
                serialized[attr_name] = getattr(self, attr_name)
        # 可选:加上__dict__中不带下划线的公开属性(如果有的话)
        for k, v in self.__dict__.items():
            if not k.startswith('_'):
                serialized[k] = v
        return json.dumps(serialized)

方法2:显式指定要序列化的属性

如果你的类属性不多,显式列出更清晰,避免意外包含不需要的属性:

import json

class MyClass(object):
    # ... 其他代码不变 ...

    def __str__(self):
        return json.dumps({
            "age": self.age,
            # 在这里添加其他需要序列化的属性
        })

额外提示

你的__getattr__方法会在访问不存在的属性时,自动给实例添加该属性并返回None,这可能会导致一些意外的属性出现在__dict__里。如果这不是你想要的行为,可以考虑去掉这个方法,或者调整逻辑(比如只返回None,不添加属性到__dict__)。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.21 04:00:48