getattr频繁调用慢是因为每次都要执行完整属性查找链,涉及多次字典查找和函数调用;缓存可提速3.2倍,适用固定对象+固定属性、固定对象+动态方法名、固定类+固定类变量三类场景。

getattr频繁调用为什么慢?
每次调用 getattr(obj, "attr") 都会触发完整的属性查找链:先查实例 __dict__,再查类、父类、描述符、__getattr__ 方法……这个过程涉及多次字典查找和函数调用,开销远高于直接访问 obj.attr。在循环或高频路径中反复调用,性能损耗会快速放大。
缓存 getattr 结果的三种可行方式
不是所有场景都适合缓存,但以下三类最常见且收益明确:
- 固定对象 + 固定属性名:比如配置对象的
debug、timeout字段,整个生命周期不变 → 可提前用getattr一次,存为局部变量或实例属性 - 固定对象 + 动态方法名(如路由分发):方法名来自字符串输入,但对象稳定 → 缓存
getattr(obj, method_name)返回的可调用对象,避免每次重新绑定 - 固定类 + 固定属性名(非实例):比如从
ConfigClass读取类变量 → 直接用getattr(ConfigClass, "VERSION")一次,而非每次都在实例上调用
别踩这些缓存陷阱
缓存本身不难,但容易忽略副作用:
仅使用 Python 将 Claw Code 虚拟化框架与 OpenClaw 集成,提供 184 种工具和 200 多条命令,用于安全分析和代码质量...
- 缓存了
getattr(obj, "prop")的返回值,但后续代码又用setattr(obj, "prop", new_val)修改了它 → 缓存值就 stale 了,必须同步更新或弃用缓存 - 误把带副作用的方法缓存成变量:例如
get_data = getattr(obj, "fetch")是对的,但写成data = getattr(obj, "fetch")()就失去缓存意义,且可能重复执行 - 在多线程环境下缓存到实例属性(如
self._cached_method),却没加锁或没考虑初始化竞态 → 可能拿到 None 或部分初始化的对象
实际对比:缓存前后耗时差异
以万次循环调用为例:
立即学习“Python免费学习笔记(深入)”;
# 未缓存(慢)
for _ in range(10000):
val = getattr(config, "timeout")
<h1>缓存后(快)</h1><p><em>timeout = getattr(config, "timeout")
for </em> in range(10000):
val = _timeout
实测在 CPython 3.12 下,后者比前者快 3.2 倍左右。差距随调用频次线性扩大,但注意:如果属性本身是计算属性(有 @property 或 __get__),缓存的是计算结果,不是“懒求值能力”——这点常被忽略。


















