我正在实现一个具有超类MyBinaryTissueClassifier
(我无法更改)的类TissueClassifier
。我试图覆盖TissueClassifier
中MyBinaryTissueClassifier
中的方法之一,但未调用我的实现。超类函数是一个cdef
函数,我正在使用Python并尝试使用def
函数进行覆盖。这就是为什么代码没有重写的原因吗?
TissueClassifier
类如下:
cdef class TissueClassifier:
*code elided*
cdef TissueClass check_point_c(self, double* point)
pass
MyBinaryTissueClassifier
类应该覆盖它,它看起来像这样:
class MyBinaryTissueClassifier(TissueClassifier):
*code elided*
def check_point_c(self, point):
*method body here*
我期望MyBinaryTissueClassifier
的主体能够执行,但从未调用过,BinaryTissueClassifier
的原始实例也从未被调用过。似乎TissueClassifier
check_point_c()方法在调用pass
之后停止。
答案 0 :(得分:2)
cdef
函数只能被派生类中的cdef
(或cpdef
)函数覆盖。这也是cython发出警告的原因:
警告:xxxxx.pyx:yy:z:用def方法覆盖cdef方法。
对于上面的示例。
通常的def
函数非常灵活:不仅必须考虑多个继承,而且还必须考虑猴子修补。但这也意味着没有太多的优化空间:Cython不能比使用python的机器多得多,即PyObject_GetAttr
/ PyObject_GenericGetAttr
+ PyMethod_GET_SELF
+ PyMethod_GET_FUNCTION
+函数的调用。
这种灵活性导致大量开销,因此cdef
函数通过使用与C ++虚拟函数相似的不同策略来避免这种情况:
cdef
类都有一个虚拟表,每个表都存储有指向cdef函数的指针。cdef
类的每个对象都有一个指向该虚拟表的指针,因此可以在运行时解析对cdef函数的调用,而解析仅花费一个间接开销。cdef
函数。cdef函数的典型调用如下:
%%cython
cdef class A:
cdef doit(self):
print(42)
def call_doit(self):
self.doit()
self.doit()
导致以下c代码:
((struct __pyx_vtabstruct_XXX_A*)__pyx_v_self->__pyx_vtab)->doit(__pyx_v_self, 0);
对象__pyx_v_self
的v表被解释为cdef类A
的v表(即使self
不是A
的实例,但的派生类,则此操作正常工作)并执行插槽doit
。
假设某个类从B
派生到A
时,它可以覆盖插槽doit
,因此调用了B的doit
版本。通过提供doit
函数的相应定义,可以覆盖cdef
槽。
正如人们所看到的,与def
函数相比,调用cdef
函数时,存在着不同的机制。
因此cdef
函数仅被其他cdef函数(而不是def
函数)覆盖。更精确地说,也可以使用cpdef
方法进行覆盖-但在您的示例中,此方法将不起作用,因为cpdef
函数不能具有double *
参数,因为它们不能自动转换Cython到Python对象,并且覆盖和覆盖的函数的签名必须匹配。