在 Cython class 中,使用 __init__ 和 __cinit__ 有什么区别?

In Cython class, what's the difference of using __init__ and __cinit__?

代码块 1 使用 __init__

%%cython -3
cdef class c:
    cdef:
        int a
        str s
    def __init__(self):
        self.a=1
        self.s="abc"
    def get_vals(self):
        return self.a,self.s
m=c()
print(m.get_vals())

代码块 2 使用 __cinit__

%%cython -3
cdef class c:
    cdef:
        int a
        str s
    def __cinit__(self):  # cinit here
        self.a=1
        self.s="abc"
    def get_vals(self):
        return self.a,self.s
m=c()
print(m.get_vals())
  1. 我测试了这两个代码,运行都没有错误。 在这种情况下,使用 __cinit__ 而不是 __init__ 有什么意义?

  2. 看了官方的文章,被一句话搞糊涂了:

    If you need to pass a modified argument list to the base type, you will have to do the relevant part of the initialization in the __init__() method instead, where the normal rules for calling inherited methods apply.

“修改参数”是什么意思?这里,为什么要用init而不是cinit呢?

  1. cinit 应该用于需要对象的 C 级初始化的地方。请注意,它们可能还不是完全有效的 python 对象。但是,任何在 cinit 中无法完成的事情都需要在 init 中完成。此时,所有对象都是有效的 python 个对象。

  2. 如果我理解正确的话,它指向派生class中的可修改参数。参数列表可能会被修改或解析为与基本类型 init 不同的方式。在这种情况下,需要使用 init。

主要是关于继承。假设我继承了你的 class C:

class D(C):
    def __init__(self):
        pass  # oops forgot to call C.__init__

class E(C):
    def __init__(self):
        super().__init__(self)
        super().__init__(self)  # called it twice

__init__ 如何最终被调用完全取决于从它继承的 classes。请记住,可能存在多层继承。

此外,a fairly common pattern for creating classes that wrap C/C++ objects is to create a staticmethod cdef function as an alternative constructor

cdef class C:
    def __cinit__(self):
        print("In __cinit__")

    @staticmethod
    cdef make_from_ptr(void* x):
        val = C.__new__(C)
        # do something with pointer
        return val

在这种情况下,通常不会调用 __init__

相比之下,__cinit__ 保证只被调用一次,这在过程的早期阶段由 Cython 自动发生。当您有 cdef 属性(例如 C 指针)而您的 class 依赖于初始化时,这一点最为重要。 Python 派生的 class 甚至不可能设置这些,但 __cinit__ 可以确保它们是。

在你的情况下,这可能并不重要 - 使用你喜欢的任何一个。


就“修改后的参数”而言,它是说你不能用 __cinit__:

复制它
class NameValue:
     def __init__(self, name, value):
         self.name = name
         self.value = value

class NamedHelloPlus1(NamedValue):
    def __init__(self, value):
        super().__init__("Hello", value+1)

NamedHelloPlus1 控制 NamedValue 获取的参数。使用 __cinit__ Cython,所有对 __cinit__ 的调用都会收到完全相同的参数(因为 Cython 会安排调用 - 您不能手动调用它)。