我正在实现一个类MyBinaryTissueClassifier
,它具有父类(super class)TissueClassifier
(我无法更改)。我试图重写 MyBinaryTissueClassifier
中的 TissueClassifier
中的方法之一,但我的实现没有被调用。父类(super class)函数是一个 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
后停止。
最佳答案
cdef
函数只能被 cdef
覆盖(或 cpdef
)派生类中的函数。这也是 cython 发出警告的原因:
warning: xxxxx.pyx:yy:z: Overriding cdef method with def method.
对于上面的例子。
通常的def
- 功能非常灵活:不仅必须考虑多重继承,例如还必须考虑猴子修补。但这也意味着没有太多的优化空间:Cython 所能做的并不比使用 python 的机制多,即 PyObject_GetAttr
/ PyObject_GenericGetAttr
+ PyMethod_GET_SELF
+ PyMethod_GET_FUNCTION
+ 函数的调用。
这种灵 active 会导致巨大的开销,因此 cdef
-函数通过使用不同的策略来避免它,这类似于C++-虚函数:
- 每个
cdef
-class 有一个虚拟表,其中存储了指向 cdef 函数的指针,每个函数都在其自己的槽中。 - 这个
cdef
的每个对象-class 有一个指向该虚拟表的指针,因此可以在运行时解析对 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);
对象的v表__pyx_v_self
被解释为 cdef 类的 v 表 A
(即使 self
不是 A
的实例,而是派生类的实例,此操作也可以正常工作)和槽 doit
已执行。
上课时,比如说B
源自A
它可以覆盖插槽 doit
,所以 B 的 doit
版本被称为。插槽doit
通过提供 cdef
的相应定义来覆盖-功能。
正如人们所看到的,当 def
时,有不同的机制在发挥作用。与 cdef
相比调用函数-功能。
因此cdef
函数只能被其他 cdef 函数覆盖(而不是 def
函数)。更准确地说,还可以使用 cpdef
- 覆盖方法 - 但在您的示例中它将不起作用,因为 cpdef
函数不能有 double *
参数,因为它们无法由 Cython 自动转换为 Python 对象,并且重写函数和被重写函数的签名必须匹配。
关于python - 是否可以用 def 覆盖 cdef 方法?覆盖方法不会执行,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/56692187/