__getattr__和__getattribute__易引发无限递归,因它们在属性访问底层被调用,内部直接访问self.xxx会再次触发自身;正确做法是用object.__getattribute__(self, name)或self.__dict__.get(name)绕过常规路径。

为什么 __getattr__ 和 __getattribute__ 容易引发无限递归
因为它们在属性访问链最底层被调用,一旦你在里面直接写 self.xxx 或 obj.xxx,就会再次触发自身,当场栈溢出。
正确做法是绕过常规访问路径,用基类方法或 object.__getattribute__ 去取值:
-
__getattribute__里一律用object.__getattribute__(self, name)获取真实属性,绝不用self.name -
__getattr__是兜底方法,只在属性不存在时才调,所以它内部可以安全用self.__dict__.get(name)或查外部映射,但别再触发属性访问 - 如果需要代理另一个对象的属性,用
type(other).__getattribute__(other, name),而不是other.name
示例错误:
def __getattribute__(self, name):<br> if name == 'cached_value':<br> return self._compute() # ❌ 这里又调了 __getattribute__
用 __slots__ 节省内存时,哪些动态行为会突然失效
__slots__ 关闭了实例的 __dict__,所有未声明的属性赋值都会报 AttributeError,连带影响依赖动态属性的常见模式。
立即学习“Python免费学习笔记(深入)”;
典型断裂点:
代替window.open、window.alert、window.confirm;提供良好的用户体验; 水晶质感,设计细腻,外观漂亮; 兼容ie6/7/8、firefox2/3、Opera;弹出框在ie6下不会被select控件穿透; 无外部css文件,引用Dialog.js即可使用; 对iframe下的应用作了充分考虑,适合复杂的系统应用; Dialog显示的内容(三种):1、指向一个URL的
- 不能用
setattr(obj, 'new_attr', value)添加未在__slots__列表里的字段 -
json.dumps(obj)会失败,因为默认序列化器靠obj.__dict__取值;得手动实现default函数或加__dict__回退逻辑 - 调试时打印
obj.__dict__得到空字典,容易误判对象状态;应改用vars(obj)(它会 fallback 到__slots__)或显式遍历__slots__ - 继承时子类必须显式声明
__slots__,否则父类的__slots__不生效;若子类想保留__dict__,得在 slots 中加入'__dict__'
元类中修改 __new__ 和 __init__ 的执行时机差异
元类的 __new__ 在类对象创建前运行,负责构造类本身;而元类的 __init__ 在类对象已生成后运行,用于“装饰”这个刚造好的类。
这意味着:
- 想拦截类定义、重写方法、注入属性——优先用元类
__new__,此时类还没诞生,你手上有原始name、bases、namespace - 想对已存在的类对象做检查或打补丁(比如验证是否实现了某个接口),用元类
__init__更自然,这时cls已是完整类对象 - 不要在元类
__init__里改cls.__dict__,它是只读的;要改就动cls的属性,或用setattr(cls, ...) - 如果同时定义了二者,
__new__必须返回一个类对象,否则__init__根本不会被调用
自定义 __eq__ 后,hash() 报错的根源和修复条件
Python 要求:如果两个对象 == 为真,它们的 hash() 必须相等。一旦你重写了 __eq__,Python 就自动把 __hash__ 设为 None,防止不一致。
要让实例可哈希,必须显式定义 __hash__,且满足:
- 只要对象参与比较的字段不变,
__hash__返回值就不能变(即这些字段得是不可变的) -
__hash__返回整数,通常用hash((self.a, self.b))元组哈希,别用id(self)—— 那会让每个实例哈希值都不同,失去集合/字典意义 - 如果类有可变字段(如列表),又想支持
__eq__,那就别实现__hash__,接受它不可哈希的事实;强行实现会导致逻辑矛盾 - 继承自
collections.abc.MutableSet等内置抽象基类时,注意它们可能隐式要求可哈希,得提前确认
复杂点在于:很多框架(比如 SQLAlchemy 的 ORM 模型)默认禁用 __hash__,因为数据库主键可能延迟加载或变更——这时候硬加 __hash__ 反而埋雷。









