У меня есть этот класс 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 мог получать параметры разных типов?
Пожалуйста, покажите мне. Я не совсем понимаю, что ты имеешь в виду
Например, def __init__(self, x, y): ...
, затем @classmethod def from_point(cls, p): return cls(p.x, p.y)
. Не перегружайте __init__
, чтобы взять x
, y
и/или Point
.
Единственное дополнение к комментарию @chepner: вместо этого вы можете использовать staticmethod
, поскольку Cython поддерживает функции cdef
staticmethod
, но не cdef classmethod
функции. Это актуально, если вам нужно передавать такие типы, как указатели C. Обратной стороной является то, что вы теряете возможность выбора cls
.
Думаю, мне нужно, чтобы вы, ребята, просто написали ответ. мне было бы легче понять
В Cython вы не можете напрямую перегружать конструкторы (или любые методы), как в C++ или других языках, поддерживающих перегрузку методов. Однако вы можете добиться аналогичной функциональности, используя фабричные методы или используя гибкость Python с аргументами.
Учитывая ваш сценарий, в котором класс Point
должен принимать различные типы параметров (объекты double
или SomeType
), вы можете реализовать эту гибкость, используя *args
и **kwargs
Python в сочетании с логикой проверки типов и обработки внутри конструктора. Кроме того, вы можете определить методы класса, которые действуют как альтернативные конструкторы, что является распространенным подходом Python для решения этой проблемы.
Вот как вы можете настроить файлы .pxd
и .pyx
в соответствии с этими требованиями:
Этот файл практически не изменился, но убедитесь, что в нем правильно указано все, что вам нужно:
# 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
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 Спасибо за ваш комментарий :) Изначально я создал from_doubles
статический метод, потому что он фокусируется на создании нового Point
объекта из предоставленных double
значений без необходимости какого-либо взаимодействия со свойствами класса или необходимости обработки поведения, специфичного для подкласса. Думаю, я больше думал о предоставлении служебной функции в пространстве имен класса Point
, подходящей для статичности из-за ее независимости от экземпляров и свойств класса. Хотя если поразмыслить, возможно, вы и правы!
Код в конструкторе не компилируется.
Не относится только к Cython, но
__init__
должен предоставлять один из способов инициализации объекта. Определите дополнительные методы класса, которые принимают аргументы других типов и преобразуют их в подходящие аргументы для__init__
.