我在 Cython 中遇到了一系列与继承相关的奇怪分段错误。
以下最小示例说明了这一点:
缓冲区.pxd
from libc.stdint cimport uint16_t, uint32_t
from cpython.mem cimport PyMem_RawCalloc,PyMem_RawFree
cdef class PixelBase:
pass
cdef class PixelBuffer(PixelBase):
cdef:
uint16_t* value
cdef void allocate(self, uint32_t len)
cdef void deallocate(self)
缓冲区.pyx
cdef class PixelBase:
def __cinit__(self, uint32_t buf_len = 100):
pass
def __init__(self, uint32_t buf_len = 100):
pass
cdef class PixelBuffer(PixelBase):
def __cinit__(self, uint32_t buf_len = 100):
self.allocate(buf_len)
def __init__(self, uint32_t buf_len = 100):
super().__init__(buf_len)
def __dealloc__(self):
self.deallocate()
cdef void allocate(self, uint32_t buf_len):
self.value = <uint16_t*> PyMem_RawCalloc(1, buf_len * sizeof(uint16_t))
cdef void deallocate(self):
PyMem_RawFree(self.value)
如果我对其进行cython化
cythonize -i buffer.pyx
并使用错误参数名称
运行测试from src.buffer import PixelBuffer
buffer = PixelBuffer(wrong_parameter_name=10)
然后它会出现段错误(不会引发异常)。删除继承(PixelBuffer 不是 PixelBase 的子类)后,会抛出适当的异常。
我遇到过与继承和使用属性相关的类似段错误问题,我试图弄清楚我的继承实现是否从根本上是错误的。
有人可以评论我从 PixelBase 继承 PixelBuffer 的方式是否存在根本性错误吗?
Python 版本 3.11.4,Cython 版本 3.0.2
当超类的
__cinit__
中引发异常时,子类的__dealloc__
将被执行。如果子类的__dealloc__
中调用了实例C方法,则会出现错误,因为C方法同时没有正确设置。 (见下文)
%%cython
cdef class PixelBase:
def __cinit__(self, int buf_len = 100):
raise Exception()
cdef class PixelBuffer(PixelBase):
def __dealloc__(self):
self.abcd() #crashed
cdef void abcd(self):
pass
此外,不建议定义在 cdef 类的
__init__
、__cinit__
、__dealloc__
、__del__
中专门调用的实例 C 方法,因为这提供的性能增益很少或没有。一段可重用的代码可以在类外部的 cdef 方法中定义,或者在 cdef 类内部由 @staticmethod
包装,这对于尝试加速时的大多数目的来说已经足够了。
%%cython
cdef class PixelBase:
def __cinit__(self, int buf_len = 100):
raise Exception()
cdef class PixelBuffer(PixelBase):
def __dealloc__(self):
PixelBuffer.abcd(self) #no problem, the exception will be raised as expected
@staticmethod
cdef void abcd(PixelBuffer a):
pass