Python getattr 与 setattr 的安全使用方式

1次阅读

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

Python getattr 与 setattr 的安全使用方式

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 requestdjango Model 实例),禁止任何反射操作,改用显式映射表转换

事情说清了就结束。真正难的不是调用这两个函数,而是判断“这个属性我有没有资格读/写”,以及“这个名字到底是谁给的”。

text=ZqhQzanResources