Cython扩展类__getattr__与Python类行为不一致,触发递归循环
解决Cython扩展类中
__getattr__的递归循环问题 这个问题我之前也碰到过,核心是Cython的cdef class和普通Python类对__dict__的处理逻辑完全不同,导致你在__getattr__里访问self.__dict__时触发了无限递归。
为什么会出现递归?
在普通Python类中,每个实例默认自带__dict__用来存储动态属性,所以访问self.__dict__不会触发__getattr__——__getattr__只有在访问不存在的属性时才会被调用。
但Cython的cdef class不一样:为了性能优化,默认情况下不会自动创建__dict__。当你在__getattr__里尝试访问self.__dict__时,因为这个属性根本不存在,Cython会再次调用__getattr__来查找__dict__,于是就形成了无限递归循环。
两种可行的解决方案
方案1:给cdef类显式添加__dict__支持
如果你确实需要用__dict__存储动态属性,可以在类定义里声明并初始化__dict__,这样它就会变成类的cdef属性,访问时不会触发__getattr__:
cdef class mockClass: cdef int _val cdef dict __dict__ # 显式声明__dict__属性 def __init__(self, val): self._val = val self.__dict__ = {} # 初始化__dict__为空字典 def __getattr__(self, attrName): print('Getattr:', attrName) print('This works in cython:', self.__dict__) try: return self.__dict__[attrName] except KeyError: raise AttributeError(f"Unknown attribute: {attrName}") def __add__(self, val): return mockClass(self._val + val) def __str__(self): return str(self._val)
方案2:绕过__getattr__直接获取属性
如果你不需要__dict__,只是想安全地获取实例属性,可以使用object.__getattribute__跳过__getattr__的触发,直接访问属性:
cdef class mockClass: cdef int _val def __init__(self, val): self._val = val def __getattr__(self, attrName): print('Getattr:', attrName) try: # 使用object的内置方法绕过__getattr__,直接查找属性 return object.__getattribute__(self, attrName) except AttributeError: raise AttributeError(f"Unknown attribute: {attrName}") def __add__(self, val): return mockClass(self._val + val) def __str__(self): return str(self._val)
额外注意点
- Cython的cdef属性(比如你的
_val)是直接内存访问的,不会触发__getattr__,只有访问未在cdef中声明的Python属性时,才会触发__getattr__。 - 如果你不需要动态添加属性,尽量不要给cdef类添加
__dict__,会损失Cython的性能优势。
内容的提问来源于stack exchange,提问作者mqnguyen1988
相关产品推荐
相关产品推荐

