__getattr__ 仅在访问不存在的属性时触发,即该属性既不在实例字典也不在类及其mro链上;它是安全轻量的兜底机制,用于懒加载、代理、友好提示等场景。

__getattr__ 什么时候会被触发
__getattr__ 只在访问**不存在的属性**时才被调用。它不是“每次取属性都走一遍”,而是 Python 的兜底机制——只有 obj.attr 查不到 attr(既不在实例字典,也不在类及其 MRO 链上),才会进 __getattr__。
容易踩的坑:__getattribute__ 是更底层的钩子,它拦截所有属性访问,滥用会导致无限递归或性能问题;而 __getattr__ 安全、轻量,专为“按需生成/代理/转发”这类场景设计。
典型使用场景包括:
- 实现懒加载属性(比如首次访问才初始化某个大对象)
- 代理到嵌套对象(如把
obj.data.name自动转成obj._inner.data.name) - 提供友好的错误提示(比如拼错属性名时给出相近建议)
- 构建 DSL 式接口(如
query.where_age_gt_18动态解析条件)
正确实现 __getattr__ 的三个前提
必须满足以下三点,否则行为不可控:
- 函数签名必须是
def __getattr__(self, name):,不能少参数,也不能加默认值 - 不能在函数体内直接访问
self.xxx同名属性(会再次触发__getattr__,死循环)——要用object.__getattribute__(self, name)或vars(self).get(name)绕过 - 如果决定不处理该
name,必须显式抛出AttributeError(不能静默返回None,否则hasattr(obj, 'xxx')会误判为 True)
示例:安全地 fallback 到字典键
让 macOS 自动化像说话一样简单 —— 用代码控制你的 Mac,无需学习 AppleScript 系统要求 macOS 14+ (Sonoma) Python 3.9+ 需要开启:辅助功能 + 屏幕录制权限
class DictLike:
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}'")
和 __getattribute__ 混用时的常见崩溃点
90% 的 __getattr__ 相关 bug 来自误以为它能覆盖所有属性访问,结果在 __getattribute__ 里又调了 self.xxx,形成递归。
如果你确实需要拦截全部访问(比如做统一日志),只用 __getattribute__;如果只是补漏,只用 __getattr__。两者混用必须极度谨慎:
- 在
__getattribute__中想读原值,必须用super().__getattribute__(name),不能写self.name - 在
__getattr__中想查是否是方法、是否有 docstring,要用object.__getattribute__(self, '__dict__')或type(self).__dict__.get(name),而不是getattr(self, name) - 继承链中多个类定义了
__getattr__,Python 只调用 MRO 中第一个找到的,不会自动链式委托
动态属性的真实代价:别在 __getattr__ 里做重活
__getattr__ 看似优雅,但每次未命中属性都会多一层函数调用 + 字符串判断。如果里面做了 IO、正则匹配、或反复构造新对象,性能会明显下滑——尤其在循环里频繁访问属性时。
优化建议:
- 把计算结果缓存到实例字典:
self.__dict__[name] = result,下次直接命中,跳过__getattr__ - 避免在
__getattr__中调用可能再次触发它的方法(比如self.__dict__.get(name)是安全的,但hasattr(self, '_cache')不安全) - 对高频访问的属性,提前在
__init__中设好,别依赖运行时动态生成
最常被忽略的一点:__getattr__ 返回的如果是可调用对象(比如函数或方法),它不会自动绑定 self——你得手动用 types.MethodType(func, self) 包一层,否则调用时少一个参数。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!










