Python使用__getattr__重载运算符时+号失效及内核崩溃问题咨询
问题解答
1. 运算符语法不生效的问题
原因
Python 对__xx__格式的魔术方法的查找逻辑和普通属性完全不同:运算符触发的魔术方法查找会直接跳过实例的属性访问流程(不会触发实例的__getattr__方法),仅在类的命名空间中查找对应方法。
你的代码中Foo类本身没有定义__add__方法,所以bar + 1语法找不到对应实现触发报错;而直接调用bar.__add__(1)属于普通属性访问,会正常触发实例的__getattr__逻辑返回对应的add方法,所以可以正常执行。
解决方法
方案1:批量挂载预定义运算符
如果你已经明确需要重载的运算符列表,可以在类定义完成后批量给类挂载魔术方法:
class Foo(): def add(self, other): return 1 + other def sub(self, other): return 1 - other # 批量绑定魔术方法到类 for op_name in ['add', 'sub']: setattr(Foo, f'__{op_name}__', getattr(Foo, op_name))
方案2:通过元类实现动态查找
如果需要动态处理更多运算符不想提前枚举,可以通过元类实现,因为魔术方法查找的是类的属性,元类的__getattr__会响应类的属性查找:
class OpMeta(type): def __getattr__(cls, name): stripped = name.strip('_') if stripped in {'add', 'sub'}: return getattr(cls, stripped) raise AttributeError class Foo(metaclass=OpMeta): def add(self, other): return 1 + other def sub(self, other): return 1 - other
2. IPython内核崩溃的问题
原因
hasattr(self, name)本身在属性不存在时会触发实例的__getattr__方法,你在__getattr__内部调用hasattr(self, name)会导致无限递归,最终栈溢出引发内核崩溃。
规避方案
不要在__getattr__内部通过hasattr访问当前实例的属性,你可以直接判断处理后的方法名是否在预定义的合法集合中(就是你最开始的写法)。如果一定要动态判断是否存在对应实现,直接访问类的属性字典绕过__getattr__触发逻辑:
def __getattr__(self, name): stripped = name.strip('_') # 直接从类的属性字典判断,不会触发__getattr__ if stripped in self.__class__.__dict__: return getattr(self, stripped) raise AttributeError
内容的提问来源于stack exchange,提问作者drmaettu
相关产品推荐
相关产品推荐

