是否可以用def覆盖cdef方法?覆盖方法将无法执行

时间:2019-06-20 18:49:59

标签: python cython

我正在实现一个具有超类MyBinaryTissueClassifier(我无法更改)的类TissueClassifier。我试图覆盖TissueClassifierMyBinaryTissueClassifier中的方法之一,但未调用我的实现。超类函数是一个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之后停止。

1 个答案:

答案 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对象,并且覆盖和覆盖的函数的签名必须匹配。