为了说明,我将使用simple Rectangle tutorial in the Cython docs。假设我添加了一个自定义c ++对象Coord
:
//Rectangle.h
class Coord {
public:
int x, y;
Coord(int x0, int y0);
~Coord();
void move(int dx, int dy);
//Omitted the rest for brevity...
};
我在Rectangle
中创建了两个公共成员变量来表示矩形的左下角和右上角:
//Rectangle.h
class Rectangle {
public:
Coord lower_left, upper_right; //added this
//Omitted the rest for brevity...
};
如果我想将Python中的Coord
操作为python对象,我该怎么做?这似乎是一件基本的事情,但我没有找到任何在线教程/指南来说明如何做到这一点。
因此,在我的尝试中,我尝试将Coord
包装为PyCoord
,以便PyRectangle
镜像c ++矩形对象。在定义中,我公开了c ++ Coord
并将它们添加为Rectangle
的成员变量,如下所示:
#rect.pyx
cdef cppclass Coord: #added this cppclass
Coord(int, int) except +
void move(int, int)
cdef cppclass Rectangle:
Rectangle(int, int, int, int) except +
Coord lower_left, upper_right #added these member variables
#The rest omitted for brevity...
这就是我在包装器中尝试做的事情:
#rect.pyx
cdef class PyCoord:
cdef Coord *thisptr
def __cinit__(self):
self.thisptr = new Coord(0,0)
def __dealloc__(self):
del self.thisptr
def move(self, int dx, int dy):
self.thisptr.move(dx, dy)
cdef class PyRectangle:
cdef Rectangle *thisptr
cdef PyCoord lower_left, upper_right #want to manipulate these in Python
def __cinit__(self, int x0, int y0, int x1, int y1):
self.thisptr = new Rectangle(x0, y0, x1, y1)
self.lower_left.thisptr = &(self.thisptr.lower_left)
self.upper_right.thisptr = &(self.thisptr.upper_right)
#The rest omitted for brevity...
所以上面的第一个问题是当我重新分配lower_left
和upper_right
时会发生内存泄漏,因为new Coord(0,0)
从未删除过,对吧?但主要的想法是能够从Python做这样的事情:
>>> from rect import *
>>> a = PyRectangle(1,1,2,2)
>>> a.upper_right.move(1,1)
Traceback (most recent call last):
File "<input>", line 1, in <module>
AttributeError: 'NoneType' object has no attribute 'move'
但正如你所看到的,我遇到了问题。实现这一目标的最佳方法是什么?必须有一些标准的方法来做到这一点,对吗?
编辑:所以看起来我可以访问像这样的原语的c ++成员变量:
#rect.pyx
cdef cppclass Coord:
int x, y #exposed x and y to cython
Coord(int, int) except +
void move(int, int)
cdef class PyCoord:
cdef Coord *thisptr
property c_x: #can use this to access C++ member x
def __get__(self):
return self.thisptr.x
def __set__(self, int i):
self.thisptr.x = i
property c_y: #can use this to access C++ member y
def __get__(self):
return self.thisptr.y
def __set__(self, int i):
self.thisptr.y = i
def __cinit__(self):
self.thisptr = new Coord(0,0)
def __dealloc__(self):
del self.thisptr
def move(self, int dx, int dy):
self.thisptr.move(dx, dy)
结果,我可以在解释器中执行此操作:
>>> from rect import *
>>> a = PyCoord()
>>> a.c_x = 1
>>> a.c_y = 2
>>> print ("(%s, %s)"%(a.c_x, a.c_y))
(1, 2)
但自定义对象类型仍然没有运气。我无法使用property
在Coord
中获取/设置PyRectangle
,或者至少不知道如何正确地执行此操作。
答案 0 :(得分:0)
您需要一种包装Coord
的方法,该方法不会声明对内存的所有权。 PyCoord
的以下修改包含对实际所有者的引用,以指示PyCoord
不应释放内存,并且只要{{1}保持实际所有者的存活活着。
PyCoord
您可以使用属性为cdef class PyCoord:
cdef Coord *thisptr
cdef object owner # None if this is our own
def __cinit__(self):
self.thisptr = new Coord(0,0)
owner = None
cdef set_ptr(self,Coord* ptr,owner):
if self.owner is None:
del self.thisptr
self.thisptr = ptr
self.owner = owner
def __dealloc__(self):
# only free if we own it
if self.owner is None:
del self.thisptr
def move(self, int dx, int dy):
self.thisptr.move(dx, dy)
PyRectangle
注意:处理许多不同的对象所有权案例当然是Boost Python所考虑的事情(我认为SWIG也考虑过这一点)所以如果你要去很多这样他们可能会提供更优雅的解决方案。