Как создать конструктор, который будет получать параметры разных типов?

У меня есть этот класс Point. Я хочу, чтобы он мог получать параметры double и SomeType.

Point.pxd:

from libcpp.memory cimport shared_ptr, weak_ptr, make_shared
from SomeType cimport _SomeType, SomeType

cdef extern from "Point.h":
    cdef cppclass _Point:
        _Point(shared_ptr[double] x, shared_ptr[double] y)
        _Point(shared_ptr[double] x, shared_ptr[double] y, shared_ptr[double] z)
        _Point(shared_ptr[_SomeType] x, shared_ptr[_SomeType] y)
        _Point(shared_ptr[_SomeType] x, shared_ptr[_SomeType] y, shared_ptr[_SomeType] z)

        shared_ptr[_SomeType] get_x()
        shared_ptr[_SomeType] get_y()
        shared_ptr[_SomeType] get_z()


cdef class Point:
    cdef shared_ptr[_Point] c_point

Point.pyx:

from Point cimport *

cdef class Point:
    def __cinit__(self, SomeType x=SomeType("0", None), SomeType y=SomeType("0", None), SomeType z=SomeType("0", None)):
        self.c_point = make_shared[_Point](x.thisptr, y.thisptr, z.thisptr)

    def __dealloc(self):
        self.c_point.reset()

    def get_x(self) -> SomeType:
        cdef shared_ptr[_SomeType] result = self.c_point.get().get_x()
        cdef SomeType coord = SomeType("", None, make_with_pointer = True)
        coord.thisptr = result
        return coord

    def get_y(self) -> SomeType:
        cdef shared_ptr[_SomeType] result = self.c_point.get().get_y()
        cdef SomeType coord = SomeType("", None, make_with_pointer = True)
        coord.thisptr = result
        return coord

    def get_z(self) -> SomeType:
        cdef shared_ptr[_SomeType] result = self.c_point.get().get_z()
        cdef SomeType coord = SomeType("", None, make_with_pointer = True)
        coord.thisptr = result
        return coord

    property x:
        def __get__(self):
            return self.get_x()

    property y:
        def __get__(self):
            return self.get_y()

    property z:
        def __get__(self):
            return self.get_z()

Как мне написать файлы .pxd и .pyx, чтобы мой конструктор Point мог получать параметры разных типов?

Не относится только к Cython, но __init__ должен предоставлять один из способов инициализации объекта. Определите дополнительные методы класса, которые принимают аргументы других типов и преобразуют их в подходящие аргументы для __init__.

chepner 30.04.2024 14:13

Пожалуйста, покажите мне. Я не совсем понимаю, что ты имеешь в виду

Punreach Rany 08.05.2024 03:14

Например, def __init__(self, x, y): ..., затем @classmethod def from_point(cls, p): return cls(p.x, p.y). Не перегружайте __init__, чтобы взять x, y и/или Point.

chepner 08.05.2024 13:22

Единственное дополнение к комментарию @chepner: вместо этого вы можете использовать staticmethod, поскольку Cython поддерживает функции cdefstaticmethod, но не cdef classmethod функции. Это актуально, если вам нужно передавать такие типы, как указатели C. Обратной стороной является то, что вы теряете возможность выбора cls.

DavidW 08.05.2024 23:18

Думаю, мне нужно, чтобы вы, ребята, просто написали ответ. мне было бы легче понять

Punreach Rany 09.05.2024 05:55
Почему в Python есть оператор "pass"?
Почему в Python есть оператор "pass"?
Оператор pass в Python - это простая концепция, которую могут быстро освоить даже новички без опыта программирования.
Некоторые методы, о которых вы не знали, что они существуют в Python
Некоторые методы, о которых вы не знали, что они существуют в Python
Python - самый известный и самый простой в изучении язык в наши дни. Имея широкий спектр применения в области машинного обучения, Data Science,...
Основы Python Часть I
Основы Python Часть I
Вы когда-нибудь задумывались, почему в программах на Python вы видите приведенный ниже код?
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
Алиса и Боб имеют неориентированный граф из n узлов и трех типов ребер:
Оптимизация кода с помощью тернарного оператора Python
Оптимизация кода с помощью тернарного оператора Python
И последнее, что мы хотели бы показать вам, прежде чем двигаться дальше, это
Советы по эффективной веб-разработке с помощью Python
Советы по эффективной веб-разработке с помощью Python
Как веб-разработчик, Python может стать мощным инструментом для создания эффективных и масштабируемых веб-приложений.
6
5
271
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

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

Учитывая ваш сценарий, в котором класс Point должен принимать различные типы параметров (объекты double или SomeType), вы можете реализовать эту гибкость, используя *args и **kwargs Python в сочетании с логикой проверки типов и обработки внутри конструктора. Кроме того, вы можете определить методы класса, которые действуют как альтернативные конструкторы, что является распространенным подходом Python для решения этой проблемы.

Вот как вы можете настроить файлы .pxd и .pyx в соответствии с этими требованиями:

  • Точка.pxd

Этот файл практически не изменился, но убедитесь, что в нем правильно указано все, что вам нужно:

# Point.pxd
from libcpp.memory cimport shared_ptr, make_shared
from SomeType cimport _SomeType, SomeType

cdef extern from "Point.h":
    cdef cppclass _Point:
        _Point(shared_ptr[double] x, shared_ptr[double] y)
        _Point(shared_ptr[double] x, shared_ptr[double] y, shared_ptr[double] z)
        _Point(shared_ptr[_SomeType] x, shared_ptr[_SomeType] y)
        _Point(shared_ptr[_SomeType] x, shared_ptr[_SomeType] y, shared_ptr[_SomeType] z)

cdef class Point:
    cdef shared_ptr[_Point] c_point
  • Point.pyx

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

# Point.pyx
from Point cimport *
from libc.stdlib cimport atof

cdef class Point:
    def __cinit__(self, *args):
        if len(args) == 2 or len(args) == 3:
            if isinstance(args[0], SomeType):
                ptrs = [arg.thisptr for arg in args]
            else:
                ptrs = [make_shared[double](atof(arg)) for arg in args]
            if len(args) == 2:
                self.c_point = make_shared[_Point](ptrs[0], ptrs[1])
            elif len(args) == 3:
                self.c_point = make_shared[_Point](ptrs[0], ptrs[1], ptrs[2])
        else:
            raise ValueError("Invalid number of arguments")

    def __dealloc__(self):
        self.c_point.reset()

    @staticmethod
    def from_doubles(x, y, z=None):
        cdef shared_ptr[double] px = make_shared[double](x)
        cdef shared_ptr[double] py = make_shared[double](y)
        cdef shared_ptr[double] pz = make_shared[double](z) if z is not None else None
        if z is None:
            return Point(px, py)
        return Point(px, py, pz)

    # ... rest of the methods ...

Здесь __cinit__ принимает переменные аргументы (*args). Он определяет тип каждого аргумента и соответствующим образом создает c_point на основе количества аргументов и их типов. Статический метод from_doubles обеспечивает более понятный и специфичный для типа способ создания экземпляров из значений типа double.

Такой подход дает вам возможность инициализировать Point объекты разных типов, сохраняя при этом чистый и читаемый код. Убедитесь, что преобразования make_shared[double](x) правильно обрабатывают ввод, и настройте проверку типов и преобразования по мере необходимости в соответствии с вашими конкретными потребностями и типами.

Нет веской причины создавать from_doubles статический метод вместо метода класса.

chepner 13.05.2024 20:02

@chepner Спасибо за ваш комментарий :) Изначально я создал from_doubles статический метод, потому что он фокусируется на создании нового Point объекта из предоставленных double значений без необходимости какого-либо взаимодействия со свойствами класса или необходимости обработки поведения, специфичного для подкласса. Думаю, я больше думал о предоставлении служебной функции в пространстве имен класса Point, подходящей для статичности из-за ее независимости от экземпляров и свойств класса. Хотя если поразмыслить, возможно, вы и правы!

Robert Long 13.05.2024 20:12

Код в конструкторе не компилируется.

DavidW 14.05.2024 08:57

Другие вопросы по теме