В чем разница между использованием __init__ и __cinit__ в классе Cython? - PullRequest
2 голосов
/ 10 июля 2020

Кодовый блок 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. Я прочитал официальную статью, меня смутило одно предложение:

    Если вам нужно передать измененный список аргументов базовому типу, вам придется вместо этого выполнить соответствующую часть инициализации в методе __init__(), где применяются обычные правила для вызова унаследованных методов.

Что означает «измененный аргумент»? Почему я должен использовать init, а не cinit?

Ответы [ 2 ]

3 голосов
/ 10 июля 2020

Это в основном о наследовании. Предположим, я наследую от вашего класса 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__ в конечном итоге вызывается, полностью зависит от классов, которые от него наследуются. Помните, что может быть несколько уровней наследования. :

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 указателей), которые ваш класс инициализирует. Для производного класса 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)

т.е. NamedHelloPlus1 контролирует, какие аргументы получает NamedValue. С __cinit__ Cython все вызовы __cinit__ получают точно такие же аргументы (потому что Cython организует вызов - вы не можете вызвать его вручную).

1 голос
/ 10 июля 2020
  1. cinit следует использовать там, где требуется инициализация уровня C объекта. Пожалуйста, будьте осторожны, они могут еще не быть полностью действующим объектом python. Однако все, что невозможно сделать в cinit , должно происходить в init . К этому времени все объекты являются действительными python объектами.

  2. , если я правильно понимаю, это указывает на изменяемые аргументы в производном классе. Список аргументов может быть изменен или проанализирован иначе, чем базовый тип init . В таких случаях необходимо использовать init. Это может быть полезно для понимания того, что я пытаюсь объяснить

...