Использование самоопределенного кода Cython из другого кода Cython

В настоящее время я пытаюсь оптимизировать свою программу на Python и начал работу с Cython, чтобы уменьшить накладные расходы на функционирование и, возможно, позже включить оптимизированные функции C-библиотек.

Итак, я столкнулся с первой проблемой:

Я использую состав в своем коде для создания более крупного класса. До сих пор я получил один из моих классов Python, преобразованный в Cython (что было достаточно сложно). Здесь код:

import numpy as np
cimport numpy as np
ctypedef np.float64_t dtype_t
ctypedef np.complex128_t cplxtype_t
ctypedef Py_ssize_t index_t

cdef class bendingForcesClass(object):
    cdef dtype_t bendingRigidity
    cdef np.ndarray matrixPrefactor
    cdef np.ndarray bendingForces

    def __init__(self, dtype_t bendingRigidity, np.ndarray[dtype_t, ndim=2] waveNumbersNorm):
        self.bendingRigidity = bendingRigidity
        self.matrixPrefactor = -self.bendingRigidity * waveNumbersNorm ** 2

    cpdef np.ndarray calculate(self, np.ndarray membraneHeight):
        cdef np.ndarray bendingForces
        bendingForces = self.matrixPrefactor * membraneHeight
        return bendingForces

Из моего составленного класса Python/Cython я вызываю метод класса calculate, так что в моем скомпонованном классе у меня есть следующий (уменьшенный) код:

from bendingForcesClass import bendingForcesClass

cdef class membraneClass(object):
    def  __init__(self, systemSideLength, lowerCutoffLength, bendingRigidity):
        self.bendingForces = bendingForcesClass(bendingRigidity, self.waveNumbers.norm)

    def calculateForces(self, heightR):
        return self.bendingForces.calculate(heightR)

Я обнаружил, что cpdef делает метод/функции вызываемыми из Python и Cython, что отлично и работает, если я не пытаюсь заранее определить тип self.bendingForces, который согласно требуется документация (Early Binding For Speed), чтобы удалить служебные служебные вызовы. Я пробовал следующее, что не работает:

from bendingForcesClass import bendingForcesClass
from bendingForcesClass cimport bendingForcesClass

    cdef class membraneClass(object):
        cdef bendingForcesClass bendingForces

        def  __init__(self, systemSideLength, lowerCutoffLength, bendingRigidity):
            self.bendingForces = bendingForcesClass(bendingRigidity, self.waveNumbers.norm)

        def calculateForces(self, heightR):
            return self.bendingForces.calculate(heightR)

С этим я получаю эту ошибку при попытке построить membraneClass.pyx с Cython:

membraneClass.pyx:18:6: 'bendingForcesClass' is not a type identifier
building 'membraneClass' extension

Обратите внимание, что объявления находятся в двух отдельных файлах, что затрудняет это.

Итак, как мне это сделать? Я был бы очень благодарен, если бы кто-то мог дать мне указатель, поскольку я не могу найти никакой информации об этом, кроме ссылки, приведенной выше.

Спасибо и с наилучшими пожеланиями!

Ответ 1

Отказ от ответственности: этот вопрос очень старый, и я не уверен, что текущее решение будет работать для кода Cython 2011 года.

Чтобы импортировать класс расширения (класс cdef) из другого файла, вам необходимо предоставить файл . pxd (также известный как определения файл), объявляющий все классы, атрибуты и методы C. См. Разделение типов расширений в документации для справки.

Для вашего примера вам понадобится файл bendingForcesClass.pxd, который объявит класс, который вы хотите предоставить, а также все cimports, переменные уровня модуля, typedefs и т.д.:

bendingForcesClass .pxd
# cimports
cimport numpy as np

# typedefy you want to share
ctypedef np.float64_t dtype_t
ctypedef np.complex128_t cplxtype_t
ctypedef Py_ssize_t index_t

cdef class bendingForcesClass:
    # declare C attributes
    cdef dtype_t bendingRigidity
    cdef np.ndarray matrixPrefactor
    cdef np.ndarray bendingForces

    # declare C functions
    cpdef np.ndarray calculate(self, np.ndarray membraneHeight)

    # note that __init__ is missing, it is not a C (cdef) function

Все импортированные, переменные и атрибуты, которые теперь объявлены в файле .pxd, могут (и должны быть) удалены из файла .pyx:

bendingForcesClass .pyx
import numpy as np

cdef class bendingForcesClass(object):

    def __init__(self, dtype_t bendingRigidity, np.ndarray[dtype_t, ndim=2] waveNumbersNorm):
        self.bendingRigidity = bendingRigidity
        self.matrixPrefactor = -self.bendingRigidity * waveNumbersNorm ** 2

    cpdef np.ndarray calculate(self, np.ndarray membraneHeight):
        cdef np.ndarray bendingForces
        bendingForces = self.matrixPrefactor * membraneHeight
        return bendingForces

Теперь ваш класс cdef bendingForcesClass может быть cimported из других модулей Cython, что делает его допустимым идентификатором типа, который должен решить вашу проблему.

Ответ 2

Вам нужно использовать файл декларации ".pxd" и cimport. (По сути, cimport происходит во время компиляции, а импорт происходит во время выполнения, поэтому Cython не может использовать ничего важного).

Создайте "utils.pxd":

cdef class MyClass:
    cdef readonly int field
    cdef void go(self, int i)

"utils.pyx" теперь читает

cdef class MyClass:
    def __init__(self, field):
    self.field = field

cdef void go(self, int i):
    self.field = i

все объявления, которые были в файле pyx, входят в файл .pxd.

Затем в mymodule.pyx

from utils import MyClass
from utils cimport MyClass
# other code follows...

//Расширенный ответ отсюда: Cython: использование импортированного класса в объявлении типа

Ответ 3

Вероятно, это не источник ошибки, но для того, чтобы сузить проблему, вы можете попробовать следующее:

Может быть, вы используете bendingForces как имя переменной здесь:

cpdef np.ndarray calculate( self, np.ndarray membraneHeight ) :
      cdef np.ndarray bendingForces
      bendingForces = self.matrixPrefactor * membraneHeight
      return bendingForces

а также имя объекта-члена здесь:

cdef class membraneClass( object ):
    cdef bendingForcesClass bendingForces

Кроме того, bendingForcesClass - это имя модуля, а также класс. Наконец, как сделать ctypedef из класса bendingForcesClass?