Cython одинаковые классы с разными типами - PullRequest
0 голосов
/ 11 апреля 2020

У меня есть два класса cdef B и C, чьи методы точно одинаковы. Единственная разница между ними заключается в типе их атрибутов: один имеет mpz атрибутов, другой int атрибутов.

Моим первым предположением было использование абстрактного класса A, который был бы переопределен B и C. Проблема в том, что Cython явно не хочет, чтобы я переопределял атрибуты (плюс, какой тип я должен дать атрибуту абстрактного класса?). При этом я получил ошибку:

------------------------------------------------------------
...

cdef class TestModularNumber:
    cdef readonly mpz value, modulo

cdef class TestInheritance(TestModularNumber):
    cdef readonly int value, modulo                     ^
------------------------------------------------------------

finite_field/testmodular.pxd:10:22: 'value' redeclared

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

ctypedef fused mpz_or_int:
    int
    mpz

Но Cython жалуется на тот факт, что я выполнять операции с этим типом (например, %, даже если он определен для обоих типов). Я получил следующие ошибки:

------------------------------------------------------------
...
ctypedef fused mpz_or_int:
    int
    mpz

cdef class TestModularNumber:
    cdef readonly mpz_or_int value, modulo                                   ^
------------------------------------------------------------

finite_field/testmodular.pxd:12:36: Type is not specialized
------------------------------------------------------------
...
from gmpy2 import invert, powmod


cdef class TestModularNumber:
    def __cinit__(self, mpz_or_int value, mpz_or_int modulo):
        self.value = value
           ^
------------------------------------------------------------

finite_field/testmodular.pyx:7:12: Invalid use of fused types, type cannot be specialized
------------------------------------------------------------
...
        if not self.has_modular_square_root():
            raise ValueError(f"{self.value} is a non-residue modulo {self.modulo}.")
        if self.value == 0 or self.value == 1:
            return TestModularNumber(self.value, self.modulo)

        if self.modulo % 4 == 3:
                      ^
------------------------------------------------------------

finite_field/testmodular.pyx:145:23: Compiler crash in AnalyseExpressionsTransform

Пока я копирую / вставляю два класса, но это грязный хак, и, очевидно, каждый раз, когда мне приходится модифицировать метод из этих классов я плачу:)

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

1 Ответ

1 голос
/ 11 апреля 2020

По причинам, объясненным в Cython: шаблоны в python оболочках класса , это обычно недоступно в Cython. cdef classes не может иметь слитых участников. В ответе на этот вопрос я предложил использовать метод «копировать / вставить», но, возможно, пытался его автоматизировать.

Для этого конкретного случая c мне интересно, могли бы вы сделать что-то лучше, взяв реализацию вне класса на отдельные cdef слитые функции. Сначала определите слитый класс для вашего cdef classes

ctypedef fused cdef_pz_or_int:
    # cdef classes _can_ be part of fused types
    TestModularNumber
    TestInheritance

Я бы порекомендовал не иметь никаких отношений наследования между классами - просто попросите их реализовать (полу) общий интерфейс.

Тогда фактическая реализация заключается в функциях, не являющихся членами:

cdef has_modular_square_root(cdef_pz_or_int self):
    value = self.value  # cython should be able to infer this type
    # more logic goes here....

Очевидно, что вы можете смешивать и сопоставлять cdef_pz_or_int и pz_or_int в качестве аргументов (если вам нужно это сделать), однако для этого, вероятно, потребуется чтобы сгенерировать действительный код для всех комбинаций.


Я не уверен на 100%, что это сработает для вас - если нет, я с удовольствием его удалю.

...