I have this Point class. I want it to be able to recieve double
and SomeType
parameters.
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()
How should I write my .pxd
and .pyx
files so that my Point constructor can receive different type of parameters?
In Cython, you cannot directly overload constructors (or any methods) as you might in C++ or other languages that support method overloading. However, you can achieve similar functionality by using factory methods or by using Python's flexibility with arguments.
Given your scenario where the Point
class needs to accept different types of parameters (either double
or SomeType
objects), you can implement this flexibility using Python's *args
and **kwargs
in combination with type checking and processing logic inside the constructor. Additionally, you can define class methods that act as alternative constructors, which is a common Pythonic approach to solve this issue.
Here’s how you might adjust your .pxd
and .pyx
files to accommodate these requirements:
This file remains largely the same but ensure it correctly declares everything you need:
# 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
Modify this file to include a flexible constructor and additional class methods for different initialisations:
# 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 ...
Here, __cinit__
accepts variable arguments (*args
). It determines the type of each argument and constructs the c_point
appropriately, based on the number of arguments and their types. The from_doubles
static method provides a clearer, type-specific way to create instances from double values.
This approach gives you the flexibility to initialise Point
objects with different types while maintaining clean, readable code. Make sure that the make_shared[double](x)
conversions handle input correctly, and adjust the type-checking and conversions as necessary for your specific needs and types.