__getattribute__ 会拦截所有属性访问,必须用 object.__getattribute__(self, name) 委托避免递归;__getattr__ 仅在属性未找到时触发,适合兜底逻辑;两者共存时 __getattribute__ 优先且可能阻止 __getattr__ 执行。

__getattribute__ 会拦截所有属性访问,包括内置属性和方法
只要类定义了 __getattribute__,Python 在每次访问实例属性(哪怕只是 obj.__dict__ 或 obj.__class__)时都会调用它。这跟 __getattr__ 完全不同——后者只在属性查找不到时才触发。
常见错误是直接在 __getattribute__ 里用 self.xxx 或 obj.xxx 访问属性,这会导致无限递归。必须用 object.__getattribute__(self, name) 向父类委托,否则连 self.__dict__ 都取不到。
实操建议:
- 除非有强需求(如统一日志、透明代理、字段加密),否则别轻易重写
__getattribute__ - 若只需处理“缺失属性”,优先用
__getattr__,更安全、开销更低 - 调试时可在开头加
if name.startswith('_') and not name.endswith('_'): raise AttributeError快速暴露意外拦截
__getattr__ 只在属性未找到时被调用,适合兜底逻辑
__getattr__ 是“后备钩子”:只有当 Python 的常规查找链(实例字典 → 类字典 → 父类 MRO)全部失败后,才会调用它。它不会干扰 __init__ 中已设置的属性,也不会影响方法调用本身(但会影响对方法对象的访问)。
典型使用场景包括动态字段映射、API 响应对象封装、惰性加载等。比如把 JSON 字典的 key 当作属性访问:
class DictAsObj:
def __init__(self, data):
self._data = data
def __getattr__(self, name):
if name in self._data:
return self._data[name]
raise AttributeError(f"'{type(self).__name__}' object has no attribute '{name}'")
注意点:
-
__getattr__不会拦截__dict__、__class__这类内置属性——它们走的是__getattribute__路径 - 如果返回了可调用对象(比如函数),它不会自动绑定为 bound method;需手动用
types.MethodType包装 - 不要在
__getattr__里无条件返回默认值(如return None),否则hasattr(obj, 'xxx')永远为True
两者共存时,__getattribute__ 总是先执行,且可能阻止 __getattr__ 触发
即使同时定义了两个方法,__getattribute__ 也会先接管一切。如果它没抛出 AttributeError,而是返回了某个值(哪怕是 None),__getattr__ 就完全没机会运行。
这意味着:如果你写了 __getattribute__ 却忘了在找不到时显式抛出 AttributeError,那么 __getattr__ 形同虚设。
实操建议:
- 共存时,
__getattribute__应该只做通用拦截(如审计、缓存),具体缺失逻辑仍交给__getattr__ - 在
__getattribute__末尾补一句raise AttributeError(...),确保 fallback 路径畅通 - 测试时用
hasattr(obj, 'missing_attr')和getattr(obj, 'missing_attr', 'default')验证是否真正进入__getattr__
性能敏感场景下,__getattribute__ 开销显著高于 __getattr__
每次属性访问都经过 __getattribute__,意味着每次都要执行你的自定义逻辑。而 __getattr__ 只在失败路径上触发,实际调用频率低得多。在高频访问(如循环内、数值计算)中,这个差异会被放大。
一个简单对比:obj.x 在有 __getattribute__ 的类中,比普通类慢 3–5 倍;而 __getattr__ 对存在属性的访问几乎无影响。
所以:
- 避免在
__getattribute__中做 I/O、正则匹配、JSON 解析等耗时操作 - 若需缓存结果,用
functools.cached_property或手动 dict 缓存,别每次调用都重新算 - 用
__slots__配合__getattribute__能略微减少字典查找开销,但无法消除方法调用本身的成本
最常被忽略的是:很多人以为加了 __getattr__ 就能“安全兜底”,却没意识到 __getattribute__ 一旦存在,就彻底改变了整个属性访问模型——它不声不响地接管了一切,而且容错极低。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











