cython cdef 的多重继承 类

Multiple inheritance of cython cdef classes

我有一些 类 在 cython 中实现为 cdef class。在客户端 python 代码中,我想用多重继承组合 类,但出现类型错误。这是一个最小的可重现示例:

In [1]: %load_ext cython

In [2]: %%cython
   ...: cdef class A:
   ...:     cdef int x
   ...:     def __init__(self):
   ...:         self.x = 0
   ...: cdef class B:
   ...:     cdef int y
   ...:     def __init__(self):
   ...:         self.y = 0
   ...: 

In [3]: class C(A, B):
   ...:     pass
   ...: 
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-3-83ef5091d3a6> in <module>()
----> 1 class C(A, B):
      2     pass

TypeError: Error when calling the metaclass bases
    multiple bases have instance lay-out conflict

有什么办法可以解决这个问题吗?

docs 说:

A Python class can inherit from multiple extension types provided that the usual Python rules for multiple inheritance are followed (i.e. the C layouts of all the base classes must be compatible).

鉴于上面的简单示例,我试图理解这可能意味着什么。

它非常受限制。据我所知,除了 classes 中的一个必须为空。空 classes 可以有 def 函数,但不能有 cdef 函数或 cdef 属性。

使用 Cython class:

cdef class A:
    cdef int x

转换为 C 代码:

 struct __pyx_obj_2bc_A { // the name might be different
     PyObject_HEAD
     int x;
 };

本质上只是一个包含基本 Python 对象内容和一个整数的 C 结构。 限制是派生的 class 必须只包含一个 PyObject_HEAD 并且它的 PyObject* 也应该可以解释为 struct __pyx_obj_2bc_A*struct __pyx_obj_2bc_B*.

在您的情况下,两个整数 xy 将尝试占用相同的内存(因此发生冲突)。但是,如果其中一种类型为空,则它们将共享 PyObject_HEAD 但不会进一步冲突。

cdef 函数导致将 struct __pyx_vtabstruct_2bc_A *__pyx_vtab; 添加到结构中(因此它不是空的)。这包含允许继承的 classes 覆盖 cdef 函数的函数指针。

有两个 cdef class 继承自共同的第三个 class 是可以的,如果共同的第三个 class 不为空。

cdef class A:
    cdef int x

cdef class B(A):
    cdef int y

cdef class C(A):
    pass

class D(B,C):
    pass

如果您真的想研究算法的细节,执行此检查的内部 Python 代码是函数 best_base


关于 "is there any way to get round this?",答案是 "not really." 你最好的选择可能是组合而不是继承(即 class C 持有 AB 对象,而不是继承自 AB)