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

o_y*_*eah 5 python oop class cython

代码块 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())
Run Code Online (Sandbox Code Playgroud)

代码块 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())
Run Code Online (Sandbox Code Playgroud)
  1. 我测试了这两个代码,并且都运行没有错误。在这种情况下,使用__cinit__而不是有__init__什么意义?

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

    如果需要将修改后的参数列表传递给基类型,则必须在方法中执行相关部分的初始化__init__(),其中适用于调用继承方法的正常规则。

“修改后的论点”是什么意思?在这里,为什么我应该使用 init 而不是 cinit?

Dav*_*idW 5

主要是关于继承。假设我从你的班级继承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
Run Code Online (Sandbox Code Playgroud)

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

此外,创建包装 C/C++ 对象的类的一个相当常见的模式是创建一个staticmethod cdef函数作为替代构造函数

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
Run Code Online (Sandbox Code Playgroud)

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

相反__cinit__,保证只调用一次,这在过程的早期由 Cython 自动发生。当您cdef的类依赖于初始化的属性(例如 C 指针)时,这一点最为重要。Python派生类甚至不可能设置这些,但__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)
Run Code Online (Sandbox Code Playgroud)

NamedHelloPlus1控制什么参数NamedValue得到。使用__cinit__Cython,所有调用都__cinit__接收完全相同的参数(因为 Cython 安排了调用 - 您不能手动调用它)。