Можно ли переопределить метод cdef с помощью def? Метод переопределения не будет выполнен - PullRequest
1 голос
/ 20 июня 2019

Я реализую класс MyBinaryTissueClassifier, который имеет суперкласс TissueClassifier (который я не могу изменить).Я пытаюсь переопределить один из методов в TissueClassifier в MyBinaryTissueClassifier, но моя реализация не вызывается.Функция суперкласса - это функция cdef, и я использую Python и пытаюсь переопределить ее с помощью функции def.Вот почему код не переопределяет?

Класс TissueClassifier выглядит следующим образом:

cdef class TissueClassifier:
    *code elided*
    cdef TissueClass check_point_c(self, double* point)
        pass

MyBinaryTissueClassifier класс должен переопределить его, и он выглядит так:

class MyBinaryTissueClassifier(TissueClassifier):
    *code elided*
    def check_point_c(self, point):
        *method body here*

Я ожидал, что тело MyBinaryTissueClassifier выполнится, но оно никогда не вызывается, как и исходный экземпляр BinaryTissueClassifier.Похоже, что метод TissueClassifier check_point_c () останавливается после вызова pass.

1 Ответ

2 голосов
/ 21 июня 2019
Функция

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

...