Функция
cdef
может быть переопределена только функцией cdef
(или cpdef
) в производном классе. Это также причина, Cython выдает предупреждение:
предупреждение: xxxxx.pyx: yy: z: переопределение метода cdef с помощью метода def.
для приведенного выше примера.
Обычная def
-функция очень гибкая: нужно учитывать не только множественное наследование, но и, например, исправление обезьян. Но это также означает, что для оптимизации не так много места: Cython может сделать гораздо больше, чем использовать механизмы python, то есть PyObject_GetAttr
/ PyObject_GenericGetAttr
+ PyMethod_GET_SELF
+ PyMethod_GET_FUNCTION
+ вызов функции.
Такая гибкость приводит к большим накладным расходам, поэтому cdef
-функции избегают ее, используя другую стратегию, аналогичную C ++ - виртуальные функции:
- Каждый класс
cdef
имеет виртуальную таблицу, в которой хранится указатель на функции cdef, каждый в своем собственном слоте.
- Каждый объект этого
cdef
-класса имеет указатель на эту виртуальную таблицу, таким образом, вызовы 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
интерпретируется как v-таблица класса cdef A
(даже если self
не является экземпляром A
, а является производным классом, эта операция работает правильно) и слот doit
выполнен.
Когда класс, скажем, B
происходит от A
, он может переопределить слот doit
, поэтому была вызвана версия B doit
. Слот doit
переопределяется путем предоставления соответствующего определения функции cdef
.
Как видно, при вызове функции def
действуют разные механизмы по сравнению с cdef
-функцией.
Таким образом, cdef
функции переопределяются только другими функциями cdef (а не def
-функциями). Чтобы быть более точным, можно также использовать cpdef
-метод для перезаписи - но в вашем примере он не будет работать, потому что cpdef
функции не могут иметь double *
аргументов, так как они не могут автоматически преобразовываться Cython в Python-объекты, и подписи переопределяющих и переопределенных функций должны совпадать.