python 3.11 的 __dict__ 查找变快,核心是重构属性查找路径、引入 load_attr_instance 字节码及内联缓存,使普通实例属性读取快10%–25%,但仅适用于未重载 __getattribute__ 的纯 python 类,且受 __dict__ 赋值等条件影响缓存失效。

__dict__ 查找变快,不是因为“存储类属性”本身被优化,而是 Python 3.11 针对**普通实例的 __dict__ 属性访问路径**做了重构和内联缓存。
为什么 LOAD_ATTR_INSTANCE 要绕过 __getattribute__ 分发?
旧版 CPython 在执行 obj.x 时,无论是否重载,都必须走完整 __getattribute__ 分发链:查 MRO、检查描述符、调用用户定义的 __getattribute__(哪怕它只是 pass)、再 fallback 到 __dict__。3.11 把这个链拆开了:LOAD_ATTR_INSTANCE 直接跳过所有分发逻辑,只做两件事——确认对象是纯 Python 实例、且 __dict__ 存在,然后哈希查找 __dict__ 字典。这省掉至少 3–4 次函数调用和字典遍历。
__slots__ 类或重载 __getattribute__ 的类为何不受益?
这些类的属性访问行为不可预测:__slots__ 没有 __dict__,重载 __getattribute__ 可能引入任意副作用(比如日志、权限检查、动态计算)。解释器无法安全地跳过它们,只能退回到通用路径。你写 obj.x 时,3.11 仍会走旧版 LOAD_ATTR,不会触发特化。
内联缓存(inline cache)实际怎么生效?
- 每个
LOAD_ATTR_INSTANCE指令首次执行时,会记录该属性名在当前类结构下的偏移量(类似 C 结构体字段 offset) - 后续访问同一属性时,直接按偏移读内存,连哈希都不用算
- 但一旦运行时执行了
setattr(obj, 'x', ...)或修改了类的__dict__结构(比如新增属性),缓存立即失效,下一次又得“冷启动”
为什么 getattr(obj, 'x') 没变快?
getattr 是纯 Python 函数,它不走 LOAD_ATTR_INSTANCE 路径,始终调用 _PyObject_GenericGetAttrWithDict,完整走一遍描述符协议、MRO 查找、__dict__ 哈希等流程。所以即使底层字节码快了,getattr 依然慢——它本质上就是“手动模拟属性访问”,没法享受内联缓存。
obj.field 访问时,10%–25% 的提升才真实可测;而一旦你混用 getattr、动态增删属性、或用了 __slots__,这些优化就自动失效了。Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











