0%

Cython返回Python类对象

在之前的一篇博客Cython调用C++函数例子中,我参照Cython文档练习了用Python通过Cython调用C++库中的函数的例子。不过这个例子中,只有一个C++类,而且所有返回对象都是系统预定义的int,void之类的。

然而我的目标是返回一个可以调用C++类函数的Python类对象。而且是在类A中生成一个类B的对象返回。

摸索一番后,我终于学会了实现这个功能的方法。
下面是把官方文档的例子简单扩展一下用来联系的例题。

C++代码

h文件

在官方文档的基础上,添加了一个Triangle类。另外把所有的int类型变量换成了double类型。
Rectangle.h:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
namespace shapes {
class Triangle {
public:
double x0, y0, x1, y1, x2, y2;
Triangle();
Triangle(double x0, double y0, double x1, double y1, double x2, double y2);
~Triangle();
double getArea();
};

class Rectangle {
public:
double x0, y0, x1, y1;
Rectangle();
Rectangle(double x0, double y0, double x1, double y1);
~Rectangle();
double getArea();
void getSize(double* width, double* height);
void move(double dx, double dy);
};


}

cpp文件

Rectangle.cpp:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
#include <math.h>
#include "Rectangle.h"

namespace shapes {

Rectangle::Rectangle() { }

Rectangle::Rectangle(double X0, double Y0, double X1, double Y1) {
x0 = X0;
y0 = Y0;
x1 = X1;
y1 = Y1;
}

Rectangle::~Rectangle() { }

double Rectangle::getArea() {
return (x1 - x0) * (y1 - y0);
}

void Rectangle::getSize(double *width, double *height) {
(*width) = x1 - x0;
(*height) = y1 - y0;
}

void Rectangle::move(double dx, double dy) {
x0 += dx;
y0 += dy;
x1 += dx;
y1 += dy;
}

Triangle::Triangle(){

}

Triangle::Triangle(double x0, double y0, double x1, double y1, double x2, double y2) {
this->x0 = x0;
this->y0 = y0;
this->x1 = x1;
this->y1 = y1;
this->x2 = x2;
this->y2 = y2;
}

Triangle::~Triangle() {}

double Triangle::getArea() {
return fabs(0.5*((x1-x0)*(y2-y0) - (x2-x0)*(y1-y0)));
}
}

pyx文件

rect.pyx:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
cdef extern from "Rectangle.h" namespace "shapes":
cdef cppclass Triangle:
Triangle() except +
Triangle(double, double, double, double, double, double) except +
double x0, y0, x1, y1, x2, y2
double getArea()


cdef cppclass Rectangle:
Rectangle() except +
Rectangle(double, double, double, double) except +
double x0, y0, x1, y1
double getArea()
void getSize(double* width, double* height)
void move(double, double)
Triangle get_lower_triangle()



cdef class PyTriangle:
cdef Triangle c_tri
def __cinit__(self, double x0, double y0, double x1, double y1, double x2, double y2):
self.c_tri = Triangle(x0, y0, x1, y1, x2, y2)
def get_area(self):
return self.c_tri.getArea()


cdef class PyRectangle:
cdef Rectangle c_rect # hold a C++ instance which we're wrapping
def __cinit__(self, double x0, double y0, double x1, double y1):
self.c_rect = Rectangle(x0, y0, x1, y1)
def get_area(self):
return self.c_rect.getArea()
def get_size(self):
cdef double width, height
self.c_rect.getSize(&width, &height)
return width, height
def move(self, dx, dy):
self.c_rect.move(dx, dy)
def get_lower_triangle(self):
return PyTriangle(self.c_rect.x0, self.c_rect.y0,
self.c_rect.x1, self.c_rect.y0,
self.c_rect.x1, self.c_rect.y1)


rect.pyx文件改动比较大。除了增加了PyTriangle类之外,还在PyRectangle类中中加了一个函数**get_lower_triangle(self)**,这个函数是原本C++类中没有的。从这一点可以更直观的理解我通过前一个例子体会到的三点收获:

  1. Cython连接C++的函数是是通过Python类的函数->cppclass->C++函数实现的。
  2. Python类的函数名称不一定要和C++函数名称一直,只要接口定义好到底调用哪个函数就行。
  3. Python类的函数数目也不一定要和C++函数一致,只需要保证Python想调用的C++函数已经定义且能够找到就行。

setup.py文件

setup.py:

1
2
3
4
5
6
7
8
from distutils.core import setup
from Cython.Build import cythonize

setup(ext_modules = cythonize(
"rect.pyx", # our Cython source
sources=["Rectangle.cpp"], # additional source file(s)
language="c++", # generate C++ code
))

这个文件的内容没有发生变化。

编译

编译方法和Cython调用C++函数例子中一样。

测试运行

修改run.py脚本:

1
2
3
4
5
6
7
8
9
import rect
#print(rect.PyRectangle(0, 0, 1, 2).get_area())

#print(rect.PyTriangle(0, 0, 0, 1, 1, 0).get_area())

rect1 = rect.PyRectangle(0.0, 0.0, 1.0, 2.0)
tri1 = rect1.get_lower_triangle()
print(type(tri1))
print(tri1.get_area())

运行后输出结果:

1
2
<type 'rect.PyTriangle'>
1.0

总结

我想返回的是Python中用的类的对象,不需要在C++类中也写一个一样的函数然后再转换。