优先用 __getattr__,仅当需拦截所有属性访问(如日志、权限)时才用 __getattribute__;后者易致无限递归,必须用 object.__getattribute__(self, name) 安全获取属性。

直接说结论:用 __getattribute__ 拦所有属性访问(包括方法、特殊方法、已存在的属性),但容易引发无限递归;用 __getattr__ 只在属性找不到时才触发,安全但覆盖范围有限。选哪个,取决于你到底想“拦截什么”。
什么时候该用 __getattribute__?
当你必须在每次点号访问(obj.attr)时都介入——比如做统一日志、权限检查、动态代理、或把所有属性转成只读——就得用它。
但它会拦截 __dict__、__class__ 这类内部属性,一不小心就死循环。关键原则是:只要你在 __getattribute__ 里用 self.xxx 或 getattr(self, xxx),基本就踩坑了。
- 必须用
object.__getattribute__(self, name)绕过自身,获取原始值 - 如果想让某个属性走默认逻辑(比如让
__len__正常工作),不能写self.__len__,得写object.__getattribute__(self, '__len__') - 抛出
AttributeError不会触发__getattr__—— 因为__getattribute__已经接管了全程
class Guard:
def __init__(self):
self.x = 10
def __getattribute__(self, name):
print(f"Accessing {name}")
# ✅ 安全获取:不触发 __getattribute__ 再次调用
return object.__getattribute__(self, name)
为什么 __getattr__ 更常用也更安全?
它只在 __getattribute__ 找不到属性(抛出 AttributeError)后才调用,天然避开递归风险,适合做“兜底逻辑”:比如动态生成属性、转发到另一个对象、或提供友好的错误提示。
让 macOS 自动化像说话一样简单 —— 用代码控制你的 Mac,无需学习 AppleScript 系统要求 macOS 14+ (Sonoma) Python 3.9+ 需要开启:辅助功能 + 屏幕录制权限
- 不会干扰已定义的实例变量、方法、或继承来的属性
- 可以放心用
self.xxx或getattr(self, 'xxx'),因为此时__getattribute__已失败退出 - 注意:它对
__len__、__iter__等协议方法无效——Python 在调用协议方法时绕过__getattr__ - 如果类里定义了
__getattribute__却没显式抛错,__getattr__根本不会运行
class LazyDict:
def __init__(self):
self._data = {}
def __getattr__(self, name):
# 只有访问不存在的属性时才进这里
if name.startswith('get_'):
key = name[4:]
return lambda: self._data.get(key)
raise AttributeError(f"'{type(self).__name__}' has no attribute '{name}'")
__getattribute__ 和 __getattr__ 同时存在时谁生效?
__getattribute__ 总是先执行;只有它明确抛出 AttributeError,且该异常未被捕捉,__getattr__ 才有机会运行。
- 如果
__getattribute__里写了try/except AttributeError并吞掉了异常,__getattr__就彻底失效 - 如果
__getattribute__返回了值(哪怕返回None),就不会再查__getattr__ - 给类加装饰器或使用
__slots__会影响属性查找链,可能让__getattr__更难触发
真正难的不是写对这两个方法,而是判断“这个需求到底需不需要碰 __getattribute__”。90% 的动态属性场景,用 __getattr__ + 显式委托就足够了;一旦动了 __getattribute__,就得对 Python 属性访问机制有肌肉记忆级别的理解——比如知道 isinstance 内部会查 __class__,而你刚把它拦下来返回了假值。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!










