不会自动报错,但默认抛出Attributeerror;必须显式传入第三个参数作fallback,否则等同obj.missing;默认值禁用可变对象,用户输入属性名需校验或白名单过滤。

getattr 读取不存在的属性时会报错吗?
不会自动报错,但默认行为是抛出 AttributeError——这恰恰是多数人踩坑的起点。你写 getattr(obj, 'missing'),没给默认值,它就炸;给了默认值(比如 getattr(obj, 'missing', None)),才安全返回。
常见错误现象:AttributeError: 'User' Object has no attribute 'age',结果程序直接中断,而本意只是“查不到就算了”。
- 必须显式传入第三个参数作为 fallback,否则等同于直接访问
obj.missing - 默认值别用可变对象(如
[]或{}),避免被意外修改共享状态 - 如果属性名来自用户输入或配置文件,务必加一层校验:先用
hasattr(obj, name)判断再getattr,或者直接靠默认值兜底
setattr 写入私有属性(_开头)或只读属性会成功吗?
会成功,python 不拦你——这是最危险的“看起来正常,其实埋雷”的点。比如对 dataclass 实例或带 @Property 的类调用 setattr(obj, '_internal_flag', True),代码不报错,但后续逻辑可能彻底失效。
使用场景:动态设置配置、反射初始化、测试 mock 对象……但一旦越过封装边界,维护成本陡增。
立即学习“Python免费学习笔记(深入)”;
-
setattr对任何属性名都尝试写入,不管是不是@property、__slots__限制,甚至__dict__不可写时也会静默失败(取决于对象实现) - 想安全赋值,先检查目标是否支持:用
hasattr(type(obj), attr_name)看是否有对应@property的 setter,或查obj.__dict__是否存在(简单对象) - 生产环境慎用
setattr修改业务模型字段;宁可用明确的update()方法或数据验证层拦截
getattr/setattr 在 getattr(obj, ‘__dict__’) 和 __slots__ 类上的表现差异
差异极大:普通类靠 __dict__ 存属性,getattr 能读所有动态添加的键;而启用 __slots__ 的类没有 __dict__,getattr 只能读预定义的 slot 名,其余一概报错(即使你用 setattr 强行塞进去,也进不了实例字典)。
性能影响:启用 __slots__ 后 getattr 略快(跳过 __dict__ 查找),但灵活性归零;兼容性上,很多依赖 __dict__ 的工具(如 copy.deepcopy、序列化库)会直接失效。
- 用
getattr(obj, '__dict__', {})安全获取属性字典,避免因__slots__导致的AttributeError - 判断类是否用了
__slots__:检查hasattr(cls, '__slots__')且'__dict__' not in cls.__slots__ - 不要在
__slots__类上依赖setattr动态新增字段——它要么失败,要么被忽略(取决于 Python 版本和定义方式)
用 getattr/setattr 做配置驱动逻辑时,如何防止执行任意代码?
当属性名来自外部(如 json 配置、URL 参数),getattr(obj, user_input) 就成了潜在的代码执行入口——如果用户传入 '__init__' 或 '__subclasses__',你就暴露了内部结构。
容易被忽略的地方:很多人只过滤下划线开头,却忘了双下划线方法(__ 开头结尾)和内置属性(如 __class__、__globals__)才是真正的风险点。
- 白名单优先:只允许一组预定义的属性名,而不是黑名单过滤
__ - 用
re.match(r'^[a-zA-Z_][a-zA-Z0-9_]*$', attr_name)做基础命名校验,再结合白名单二次确认 - 对敏感对象(如 flask
request、djangoModel实例),禁止任何反射操作,改用显式映射表转换
事情说清了就结束。真正难的不是调用这两个函数,而是判断“这个属性我有没有资格读/写”,以及“这个名字到底是谁给的”。