Cython одни и те же классы с разными типами

У меня есть два класса 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

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

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


person Tristan Nemoz    schedule 11.04.2020    source источник
comment
Это довольно похожая проблема stackoverflow.com/ вопросы/31436593/. К сожалению, я не думаю, что появились какие-либо новые решения. В основном вам, вероятно, нужно копировать и вставлять, но вы можете попробовать автоматизировать это   -  person DavidW    schedule 11.04.2020
comment
Блин, надеялся, что будет более красивое решение :'( Спасибо, тем не менее!   -  person Tristan Nemoz    schedule 11.04.2020
comment
Это определенно было бы неплохо. На самом деле у меня есть еще одна мысль, которая может сработать для вашего конкретного случая...   -  person DavidW    schedule 11.04.2020
comment
Действительно? Каков был бы обходной путь или, по крайней мере, принцип, чтобы я тоже мог подумать об этом?   -  person Tristan Nemoz    schedule 11.04.2020
comment
Я разместил предложение в качестве ответа. Это довольно расплывчато и неполно, но я думаю, что это должно помочь вам избежать некоторого дублирования кода. Если окажется, что это не работает, то скажите мне, и я избавлюсь от этого (чтобы никого не вводить в заблуждение)   -  person DavidW    schedule 11.04.2020
comment
К концу дня обязательно попробую и тогда скажу. Спасибо!   -  person Tristan Nemoz    schedule 11.04.2020


Ответы (1)


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

В этом конкретном случае мне интересно, не могли бы вы сделать немного лучше, вынеся реализацию из класса в отдельные 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%, что это сработает для вас - если нет, я буду рад удалить его.

person DavidW    schedule 11.04.2020
comment
Просто чтобы быть уверенным (я совсем новичок в Cython): если этот метод работает, то у меня будут рабочие функции, но эти функции не будут доступны как методы, верно? Кроме того, большая часть кода класса (на самом деле все, кроме метода квадратного корня) — это волшебные функции для обработки таких операторов, как __add__. Следовательно, могу ли я действительно использовать этот метод? Или я неправильно понял? - person Tristan Nemoz; 11.04.2020
comment
Я думаю, вы правильно понимаете. В зависимости от того, насколько велико содержимое функций, это может быть полезно - вы можете определить def __add__(self, other): return add_function(self, other). Если в add_function много кода, то оно того стоит. Если нет, то это не так. Хотя похоже, что это может не работать для вас, хотя - person DavidW; 11.04.2020