直接查 dict 能绕过整个属性查找链,因为它直击实例或类的存储字典,跳过__getattribute__、MRO查找、描述符协议等全部机制;但需注意__slots__类无__dict__,且__dict__只反映存储位置,不决定访问行为。

直接查 __dict__ 能绕过整个属性查找链
每次用 obj.attr 访问属性,Python 都会走完整查找路径:先调 __getattribute__,再查实例 __dict__,再按 MRO 逐个查类及其父类的 __dict__(还要区分描述符类型),最后才可能触发 __getattr__。而直接读 obj.__dict__ 或 MyClass.__dict__,等于跳过所有中间环节,直击存储位置。
常见错误现象:print(obj.x) 显示值正常,但 obj.__dict__.get('x') 返回 None —— 这说明 x 是类属性,不在实例字典里;反过来,obj.__dict__ 里有 'y' 但 obj.y 报 AttributeError,大概率是 __getattribute__ 被重写且没正确委托。
__dict__ 是真实存储容器,不是“快照”或代理
实例的 __dict__ 就是一个普通 dict,增删改查都是原生字典操作;类的 __dict__ 是 mappingproxy,只读但内容完全真实——它不隐藏、不转换、不延迟计算,看到的就是当前内存中确切存在的键值对。
使用场景:
- 调试时快速确认某个属性到底存在在哪一层:查
obj.__dict__没有 → 查type(obj).__dict__→ 再查type(obj).__mro__[1].__dict__ - 批量检查实例是否意外携带了不该有的动态属性(比如漏掉
self.导致写成局部变量) - 序列化前过滤掉方法、描述符等非数据项:
{k: v for k, v in obj.__dict__.items() if not callable(v)}
注意 __slots__ 会让 __dict__ 彻底消失
如果类定义了 __slots__ = ['a', 'b'],实例将没有 __dict__ 属性,访问 obj.__dict__ 直接抛 AttributeError。这不是 bug,是设计使然——启用 __slots__ 后,属性值被存进固定偏移的内存块,不再需要字典结构。
容易踩的坑:
- 误以为所有对象都有
__dict__,结果在__slots__类上做反射操作失败 - 用
hasattr(obj, '__dict__')判断是否可动态赋值,但某些内置类型(如int、str)也无__dict__,和__slots__无关 - 试图通过
obj.__dict__.update(...)批量设属性,却忽略__slots__类根本走不通这条路
查 __dict__ 不等于理解属性行为
__dict__ 只告诉你“值存在哪”,不告诉你“怎么取出来”。比如一个 @property 方法,在类的 __dict__ 里是 property 对象,但你不能直接从那里拿到计算后的值;又比如 __getattribute__ 重写后,即使 obj.__dict__ 里有 'x',obj.x 也可能返回别的东西。
所以排查时得配合使用:
- 看值在哪:直接读
obj.__dict__和type(obj).__dict__ - 看怎么取:检查是否定义了
__getattribute__、__getattr__、@property或描述符 - 看有没有被拦截:临时注释掉自定义的
__getattribute__,再试obj.x
真正难的从来不是找到值的位置,而是搞清那个点号背后发生了多少层跳转。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











