安全使用 getattr 需传入默认值(如 ... 或 object())以避免 AttributeError;setattr 受 __slots__、不可变类型等限制,修改类属性须作用于类而非实例;dataclass 冻结后不可 setattr,NamedTuple 完全不可变;外部配置驱动时须校验属性名白名单与类型转换。

getattr 怎么安全获取对象属性,避免 AttributeError?
直接用 obj.attr 访问不存在的属性会抛 AttributeError,而 getattr 可以兜底。它接受三个参数:对象、属性名字符串、默认值(可选)。没提供默认值且属性不存在时,仍会报错;加了默认值就完全静默。
常见错误是把属性名写成变量但忘了加引号,比如误写 getattr(obj, my_var) 却没确保 my_var 是字符串;或者默认值设为 None 后,无法区分“属性真为 None”和“属性根本不存在”。
- 推荐始终传第三个参数,哪怕只是
...(占位符)或object()这类唯一对象,方便后续判断 - 若需批量检查多个属性,别反复调
getattr,先用hasattr判断再取值,减少异常开销(尤其在循环里) -
getattr不触发 property 的__get__吗?会的——它走的是标准属性访问协议,@property和描述符都正常生效
setattr 设置属性时,为什么有时不生效或报错?
setattr 看似简单,但实际受对象是否允许动态赋值限制。比如内置类型(int、str)、某些 C 扩展类、或定义了 __slots__ 且未包含目标属性名的类,都会抛 AttributeError。
另一个典型坑是:对实例设置属性成功了,但期望影响类行为(比如改类变量),结果发现只是给实例加了个同名实例属性,遮蔽了类属性。
立即学习“Python免费学习笔记(深入)”;
- 用
setattr(obj, 'x', v)前,先确认obj是可变对象,且目标属性名在__slots__中(如有) - 想改类属性,得对类本身调
setattr(cls, 'attr', value),而不是对实例 - 不要用
setattr绕过@property的 setter——它会跳过验证逻辑,直接写入底层__dict__,可能破坏对象一致性
getattr/setattr 在字典式对象(如 dataclass、SimpleNamespace)中怎么用?
像 types.SimpleNamespace 或未冻结的 @dataclass 实例,本质是普通 Python 对象,getattr/setattr 完全适用。但要注意:如果 dataclass 用了 frozen=True,setattr 会立刻失败,而 getattr 仍可读。
更隐蔽的问题是字段名含特殊字符或以数字开头(如 "2nd_item"),虽然 Python 标识符不允许,但 dataclass 允许字段名是任意字符串(通过 field(name="2nd_item")),这时 getattr 仍能按名取值,但点号访问语法失效。
- 对 dataclass,优先用
dataclasses.asdict()转字典再操作,语义更清晰;反射仅用于字段名动态生成的场景(如从 JSON key 映射) -
SimpleNamespace没有隐藏字段,所有属性都在__dict__里,getattr和直接查__dict__.get()行为一致,但前者更符合封装原则 - 别对
NamedTuple用setattr——它是不可变的,连__dict__都没有
用 getattr/setattr 做配置驱动时,哪些边界情况必须检查?
当配置项来自外部(如 YAML、数据库字段),属性名是运行时字符串,这时 getattr/setattr 成了刚需。但风险也集中:拼写错误、类型错配、权限越界(比如把私有属性名 "_secret" 当公开字段暴露出去)。
最常被忽略的是属性值的类型转换。配置里读出的 "true" 是字符串,但目标属性可能是 bool,setattr 不会自动转,直接塞进去可能导致逻辑错误。
- 永远校验属性名是否在白名单内,尤其涉及用户输入时,防
__dict__注入或私有属性泄露 - 对数值、布尔等类型,先用
ast.literal_eval或专用解析器转换字符串,再传给setattr - 如果对象有自定义
__setattr__,setattr会触发它;不确定时,可用object.__setattr__(obj, name, value)绕过(慎用)
反射不是黑魔法,它把编译期检查推到了运行期。每次调用 getattr 或 setattr,你都得自己承担名字拼错、类型错、权限错的全部后果。


















