1. 用Cython包装C++代码
Cython最大的作用其实是作为C/C++代码和python代码的桥梁,比如我们已经有一个C++写的程序了,但我们希望让python可以调用它,传统的做法是使用ctypes或者cffi作为桥,但这种方式需要有相当的C/C++知识.Cython的话基本可以无痛进行C++代码的包装,这是通过使用外部声明来声明库函数和要使用的库中的C函数来实现的.
Cython现在原生的支持大多数的C++语法. 尤其是: 现在可以使用new
和del
关键字动态分配C++对象.
- C++对象可以进行堆栈分配
- C++类可以使用新的关键字
cppclass
声明 - 支持模板化类和函数
- 支持重载函数
- 支持C++操作符(例如operator +,operator [],...)的重载
我们通过包装一个例子来看看cython是如何包装c/c++代码的
1.1. 封装步骤
封装C/C++的步骤大致有如下几步:
- 在setup.py脚本中或在源文件中本地指定C ++语言。
- 使用
cdef extern from 头文件
创建一个或多个.pxd
文件.在pxd文件中,以cdef cppclass
来声明类并且声明公共名称(变量,方法和构造函数) - 通过
cimport
引入pxd
文件,进行pxd
的实现代码,也就是.pyx
文件。
1.2. 最简单的一个例子
这个例子用来介绍Cython包装C/C++代码的步骤.例子是一个长方形类,C++代码部分如下:
%load_ext Cython
%%writefile Rectangle.h namespace shapes { class Rectangle { public: static int do_something(); int x0, y0, x1, y1; Rectangle(); Rectangle(int x0, int y0, int x1, int y1); ~Rectangle(); int getArea(); void getSize(int* width, int* height); void move(int dx, int dy); }; }
Overwriting Rectangle.h
%%writefile Rectangle.cpp #include "Rectangle.h" namespace shapes { Rectangle::Rectangle() { } int Rectangle::do_something(){ return 0; } Rectangle::Rectangle(int X0, int Y0, int X1, int Y1) { x0 = X0; y0 = Y0; x1 = X1; y1 = Y1; } Rectangle::~Rectangle() { } int Rectangle::getArea() { return (x1 - x0) * (y1 - y0); } void Rectangle::getSize(int *width, int *height) { (*width) = x1 - x0; (*height) = y1 - y0; } void Rectangle::move(int dx, int dy) { x0 += dx; y0 += dy; x1 += dx; y1 += dy; } }
Overwriting Rectangle.cpp
1.3. 用于包装的pyx文件
要包装C++文件,我们得先在cython中声明出这个C++的类,在cython中申明C或者C++的内容(接口)需要使用cdef extern from ....
这种语法(外部声明).
在
%%writefile rect.pyx #cython: language_level=3 # distutils: language = c++ # distutils: sources = Rectangle.cpp cdef extern from "Rectangle.h" namespace "shapes": cdef cppclass Rectangle: Rectangle() except + Rectangle(int, int, int, int) except + int x0, y0, x1, y1 int getArea() void getSize(int* width, int* height) void move(int, int) @staticmethod int do_something() cdef class PyRectangle: cdef Rectangle c_rect # hold a C++ instance which we're wrapping def __cinit__(self, int x0, int y0, int x1, int y1): self.c_rect = Rectangle(x0, y0, x1, y1) def get_area(self): return self.c_rect.getArea() def get_size(self): cdef int width, height self.c_rect.getSize(&width, &height) return width, height def move(self, dx, dy): self.c_rect.move(dx, dy) @staticmethod def do_something(): return Rectangle.do_something()
Overwriting rect.pyx
这样,我们就完成了C++的封装。而且从Python的开发角度来看,这个扩展类型看起来和感觉就像一个本地定义的Rectangle类。 需要注意的是,如果我们需要额外的属性设置方法,可以自己再添加.
1.4. setup.py的写法
我们的setup.py和之前差不多的写法
%%writefile setup.py from distutils.core import setup from Cython.Build import cythonize setup( name = "rectangleapp", ext_modules = cythonize('*.pyx') )
Overwriting setup.py
!python setup.py build_ext --inplace
%%writefile Rectangle.h namespace shapes { class Rectangle { public: static int do_something(); int x0, y0, x1, y1; Rectangle(); Rectangle(int x0, int y0, int x1, int y1); ~Rectangle(); int getArea(); void getSize(int* width, int* height); void move(int dx, int dy); }; }
0
%%writefile Rectangle.h namespace shapes { class Rectangle { public: static int do_something(); int x0, y0, x1, y1; Rectangle(); Rectangle(int x0, int y0, int x1, int y1); ~Rectangle(); int getArea(); void getSize(int* width, int* height); void move(int dx, int dy); }; }
1
%%writefile Rectangle.h namespace shapes { class Rectangle { public: static int do_something(); int x0, y0, x1, y1; Rectangle(); Rectangle(int x0, int y0, int x1, int y1); ~Rectangle(); int getArea(); void getSize(int* width, int* height); void move(int dx, int dy); }; }
2
%%writefile Rectangle.h namespace shapes { class Rectangle { public: static int do_something(); int x0, y0, x1, y1; Rectangle(); Rectangle(int x0, int y0, int x1, int y1); ~Rectangle(); int getArea(); void getSize(int* width, int* height); void move(int dx, int dy); }; }
3
%%writefile Rectangle.h namespace shapes { class Rectangle { public: static int do_something(); int x0, y0, x1, y1; Rectangle(); Rectangle(int x0, int y0, int x1, int y1); ~Rectangle(); int getArea(); void getSize(int* width, int* height); void move(int dx, int dy); }; }
4
%%writefile Rectangle.h namespace shapes { class Rectangle { public: static int do_something(); int x0, y0, x1, y1; Rectangle(); Rectangle(int x0, int y0, int x1, int y1); ~Rectangle(); int getArea(); void getSize(int* width, int* height); void move(int dx, int dy); }; }
5
%%writefile Rectangle.h namespace shapes { class Rectangle { public: static int do_something(); int x0, y0, x1, y1; Rectangle(); Rectangle(int x0, int y0, int x1, int y1); ~Rectangle(); int getArea(); void getSize(int* width, int* height); void move(int dx, int dy); }; }
6
%%writefile Rectangle.h namespace shapes { class Rectangle { public: static int do_something(); int x0, y0, x1, y1; Rectangle(); Rectangle(int x0, int y0, int x1, int y1); ~Rectangle(); int getArea(); void getSize(int* width, int* height); void move(int dx, int dy); }; }
7
1.5. 外部声明
默认情况下,在模块级声明的C函数和变量对模块是本地的(即它们具有C静态存储类).它们也可以声明为extern,以指定它们在其他位置定义,例如:
%%writefile Rectangle.h namespace shapes { class Rectangle { public: static int do_something(); int x0, y0, x1, y1; Rectangle(); Rectangle(int x0, int y0, int x1, int y1); ~Rectangle(); int getArea(); void getSize(int* width, int* height); void move(int dx, int dy); }; }
8
Cython包装C/C++就是依赖这种外部申明
1.6. 引用头文件
当你使用一个extern定义时,Cython在生成的C文件中包含一个声明.如果声明与其他C代码将看不到的声明不完全匹配,这可能会导致问题.例如,如果要封装现有的C库,那么生成的C代码必须与库的其余部分具有完全相同的声明.
为了实现这一点,你可以告诉Cython声明将在C头文件中找到,如下所示:
%%writefile Rectangle.h namespace shapes { class Rectangle { public: static int do_something(); int x0, y0, x1, y1; Rectangle(); Rectangle(int x0, int y0, int x1, int y1); ~Rectangle(); int getArea(); void getSize(int* width, int* height); void move(int dx, int dy); }; }
9
引用头文件用于引入C/C++中的声明,但我们依然需要手动将其中被声明的内容用cython语法重新写一遍,这样cython才可以识别.
这个cdef extern
代码块定义了如下三件事情:
- 它指示Cython为生成的C代码中的命名头文件放置一个#include语句.
- 它阻止Cython为相关块中的声明生成任何C代码
- 它处理块中的所有声明,就像它们以cdef extern开头
重要的是要理解Cython本身不读取C头文件,所以你仍然需要提供Cython版本你要使用的声明.然而,Cython声明并不总是必须完全匹配C,在某些情况下,它们不应该或不能。尤其是:
- 不要使用任何平台特定的C语言扩展,例如
__declspec()
如果头文件声明一个大结构,并且你只想使用几个成员,你只需要声明你感兴趣的成员.留下余下的没有任何危害,因为C编译器将使用头文件中的完整定义.
在某些情况下,你可能不需要任何struct的成员,在这种情况下,你可以只传递在struct声明的主体,例如:
Overwriting Rectangle.h
0注意:你只能在一个cdef extern从块里面这样做;任何其他地方的struct声明必须是非空的。
如果头文件使用
typedef
名称(如word)来引用与平台相关的数值类型的风格,则需要一个相应的ctypedef
语句,但不需要完全匹配类型,只是使用一些正确的一般类型(int,float等). 例如:Overwriting Rectangle.h
1将工作正常无论实际大小的单词是(提供的头文件正确定义它).与Python类型(如果有)之间的转换也将用于此新类型.
如果头文件使用宏来定义常量,则将它们转换为正常的外部变量声明。如果它们包含正常的int值,也可以将它们声明为枚举。请注意,Cython认为枚举等同于int,因此不要对非int值执行此操作.
如果头文件使用宏定义了一个函数,那么声明它就像是一个普通的函数,具有适当的参数和结果类型
如果你想包含一个C头,因为它是另一个头需要的,但不想使用它的任何声明,在extern-from块中放入pass关键字:
Overwriting Rectangle.h
2如果要包括系统标题,请在引号中加上尖括号:
Overwriting Rectangle.h
3如果你想包含一些外部声明,但不想指定一个头文件(因为它包含了你已经包含的其他头文件),你可以用*代替头文件名:
Overwriting Rectangle.h
4
1.7. 在C/C++中实现
另一种简单的写法是直接使用外部声明声明C/C++实现
Overwriting Rectangle.h
5
Overwriting Rectangle.h
6
Overwriting Rectangle.h
7
Overwriting Rectangle.h
8
Overwriting Rectangle.h
9
Overwriting setup.py
!python setup.py build_ext --inplace
%%writefile Rectangle.cpp #include "Rectangle.h" namespace shapes { Rectangle::Rectangle() { } int Rectangle::do_something(){ return 0; } Rectangle::Rectangle(int X0, int Y0, int X1, int Y1) { x0 = X0; y0 = Y0; x1 = X1; y1 = Y1; } Rectangle::~Rectangle() { } int Rectangle::getArea() { return (x1 - x0) * (y1 - y0); } void Rectangle::getSize(int *width, int *height) { (*width) = x1 - x0; (*height) = y1 - y0; } void Rectangle::move(int dx, int dy) { x0 += dx; y0 += dy; x1 += dx; y1 += dy; } }
2
%%writefile Rectangle.cpp #include "Rectangle.h" namespace shapes { Rectangle::Rectangle() { } int Rectangle::do_something(){ return 0; } Rectangle::Rectangle(int X0, int Y0, int X1, int Y1) { x0 = X0; y0 = Y0; x1 = X1; y1 = Y1; } Rectangle::~Rectangle() { } int Rectangle::getArea() { return (x1 - x0) * (y1 - y0); } void Rectangle::getSize(int *width, int *height) { (*width) = x1 - x0; (*height) = y1 - y0; } void Rectangle::move(int dx, int dy) { x0 += dx; y0 += dy; x1 += dx; y1 += dy; } }
3
%%writefile Rectangle.cpp #include "Rectangle.h" namespace shapes { Rectangle::Rectangle() { } int Rectangle::do_something(){ return 0; } Rectangle::Rectangle(int X0, int Y0, int X1, int Y1) { x0 = X0; y0 = Y0; x1 = X1; y1 = Y1; } Rectangle::~Rectangle() { } int Rectangle::getArea() { return (x1 - x0) * (y1 - y0); } void Rectangle::getSize(int *width, int *height) { (*width) = x1 - x0; (*height) = y1 - y0; } void Rectangle::move(int dx, int dy) { x0 += dx; y0 += dy; x1 += dx; y1 += dy; } }
4
%%writefile Rectangle.cpp #include "Rectangle.h" namespace shapes { Rectangle::Rectangle() { } int Rectangle::do_something(){ return 0; } Rectangle::Rectangle(int X0, int Y0, int X1, int Y1) { x0 = X0; y0 = Y0; x1 = X1; y1 = Y1; } Rectangle::~Rectangle() { } int Rectangle::getArea() { return (x1 - x0) * (y1 - y0); } void Rectangle::getSize(int *width, int *height) { (*width) = x1 - x0; (*height) = y1 - y0; } void Rectangle::move(int dx, int dy) { x0 += dx; y0 += dy; x1 += dx; y1 += dy; } }
5
需要注意的是这种方式函数必须是static
1.8. 结构,联合和枚举声明的样式
在C头文件中可以使用两种主要方法来声明结构,联合和枚举:
- 使用标签名称
- 使用typedef
基于这些的各种组合也存在一些变化.重要的是使Cython声明与头文件中使用的样式相匹配,以便Cython能够对其生成的代码中的类型发出正确的引用. 为了实现这一点,Cython提供了两种不同的语法来声明结构,联合或枚举类型.上面介绍的样式对应于标签名的使用.要获得另一个样式,您需要在声明前面加上ctypedef
,如下图所示. 下表显示了可以在头文件中找到的各种可能的样式,以及应该放在cdef extern from
块中的相应Cython声明。用结构体声明作为例子;这同样适用于联合和枚举声明.
1.9. 静态成员方法
如果开头我们定义的C++类Rectangle类具有静态成员,那么如上面的做法..就像python中一样,使用@staticmethod
装饰器装饰对应的成员方法即可
1.10. 运算符重载
这个例子是一个vector2d,实现了加和乘.
%%writefile Rectangle.cpp #include "Rectangle.h" namespace shapes { Rectangle::Rectangle() { } int Rectangle::do_something(){ return 0; } Rectangle::Rectangle(int X0, int Y0, int X1, int Y1) { x0 = X0; y0 = Y0; x1 = X1; y1 = Y1; } Rectangle::~Rectangle() { } int Rectangle::getArea() { return (x1 - x0) * (y1 - y0); } void Rectangle::getSize(int *width, int *height) { (*width) = x1 - x0; (*height) = y1 - y0; } void Rectangle::move(int dx, int dy) { x0 += dx; y0 += dy; x1 += dx; y1 += dy; } }
6
%%writefile Rectangle.cpp #include "Rectangle.h" namespace shapes { Rectangle::Rectangle() { } int Rectangle::do_something(){ return 0; } Rectangle::Rectangle(int X0, int Y0, int X1, int Y1) { x0 = X0; y0 = Y0; x1 = X1; y1 = Y1; } Rectangle::~Rectangle() { } int Rectangle::getArea() { return (x1 - x0) * (y1 - y0); } void Rectangle::getSize(int *width, int *height) { (*width) = x1 - x0; (*height) = y1 - y0; } void Rectangle::move(int dx, int dy) { x0 += dx; y0 += dy; x1 += dx; y1 += dy; } }
7
%%writefile Rectangle.cpp #include "Rectangle.h" namespace shapes { Rectangle::Rectangle() { } int Rectangle::do_something(){ return 0; } Rectangle::Rectangle(int X0, int Y0, int X1, int Y1) { x0 = X0; y0 = Y0; x1 = X1; y1 = Y1; } Rectangle::~Rectangle() { } int Rectangle::getArea() { return (x1 - x0) * (y1 - y0); } void Rectangle::getSize(int *width, int *height) { (*width) = x1 - x0; (*height) = y1 - y0; } void Rectangle::move(int dx, int dy) { x0 += dx; y0 += dy; x1 += dx; y1 += dy; } }
8
%%writefile Rectangle.cpp #include "Rectangle.h" namespace shapes { Rectangle::Rectangle() { } int Rectangle::do_something(){ return 0; } Rectangle::Rectangle(int X0, int Y0, int X1, int Y1) { x0 = X0; y0 = Y0; x1 = X1; y1 = Y1; } Rectangle::~Rectangle() { } int Rectangle::getArea() { return (x1 - x0) * (y1 - y0); } void Rectangle::getSize(int *width, int *height) { (*width) = x1 - x0; (*height) = y1 - y0; } void Rectangle::move(int dx, int dy) { x0 += dx; y0 += dy; x1 += dx; y1 += dy; } }
9
Overwriting Rectangle.cpp
0
Overwriting Rectangle.cpp
1
Overwriting Rectangle.cpp
2
Overwriting Rectangle.cpp
3
Overwriting Rectangle.cpp
4
Overwriting Rectangle.cpp
5
Overwriting Rectangle.cpp
6
Overwriting Rectangle.cpp
7
Overwriting setup.py
!python setup.py build_ext --inplace
%%writefile rect.pyx #cython: language_level=3 # distutils: language = c++ # distutils: sources = Rectangle.cpp cdef extern from "Rectangle.h" namespace "shapes": cdef cppclass Rectangle: Rectangle() except + Rectangle(int, int, int, int) except + int x0, y0, x1, y1 int getArea() void getSize(int* width, int* height) void move(int, int) @staticmethod int do_something() cdef class PyRectangle: cdef Rectangle c_rect # hold a C++ instance which we're wrapping def __cinit__(self, int x0, int y0, int x1, int y1): self.c_rect = Rectangle(x0, y0, x1, y1) def get_area(self): return self.c_rect.getArea() def get_size(self): cdef int width, height self.c_rect.getSize(&width, &height) return width, height def move(self, dx, dy): self.c_rect.move(dx, dy) @staticmethod def do_something(): return Rectangle.do_something()
0
%%writefile rect.pyx #cython: language_level=3 # distutils: language = c++ # distutils: sources = Rectangle.cpp cdef extern from "Rectangle.h" namespace "shapes": cdef cppclass Rectangle: Rectangle() except + Rectangle(int, int, int, int) except + int x0, y0, x1, y1 int getArea() void getSize(int* width, int* height) void move(int, int) @staticmethod int do_something() cdef class PyRectangle: cdef Rectangle c_rect # hold a C++ instance which we're wrapping def __cinit__(self, int x0, int y0, int x1, int y1): self.c_rect = Rectangle(x0, y0, x1, y1) def get_area(self): return self.c_rect.getArea() def get_size(self): cdef int width, height self.c_rect.getSize(&width, &height) return width, height def move(self, dx, dy): self.c_rect.move(dx, dy) @staticmethod def do_something(): return Rectangle.do_something()
1
%%writefile rect.pyx #cython: language_level=3 # distutils: language = c++ # distutils: sources = Rectangle.cpp cdef extern from "Rectangle.h" namespace "shapes": cdef cppclass Rectangle: Rectangle() except + Rectangle(int, int, int, int) except + int x0, y0, x1, y1 int getArea() void getSize(int* width, int* height) void move(int, int) @staticmethod int do_something() cdef class PyRectangle: cdef Rectangle c_rect # hold a C++ instance which we're wrapping def __cinit__(self, int x0, int y0, int x1, int y1): self.c_rect = Rectangle(x0, y0, x1, y1) def get_area(self): return self.c_rect.getArea() def get_size(self): cdef int width, height self.c_rect.getSize(&width, &height) return width, height def move(self, dx, dy): self.c_rect.move(dx, dy) @staticmethod def do_something(): return Rectangle.do_something()
2
%%writefile rect.pyx #cython: language_level=3 # distutils: language = c++ # distutils: sources = Rectangle.cpp cdef extern from "Rectangle.h" namespace "shapes": cdef cppclass Rectangle: Rectangle() except + Rectangle(int, int, int, int) except + int x0, y0, x1, y1 int getArea() void getSize(int* width, int* height) void move(int, int) @staticmethod int do_something() cdef class PyRectangle: cdef Rectangle c_rect # hold a C++ instance which we're wrapping def __cinit__(self, int x0, int y0, int x1, int y1): self.c_rect = Rectangle(x0, y0, x1, y1) def get_area(self): return self.c_rect.getArea() def get_size(self): cdef int width, height self.c_rect.getSize(&width, &height) return width, height def move(self, dx, dy): self.c_rect.move(dx, dy) @staticmethod def do_something(): return Rectangle.do_something()
3
%%writefile rect.pyx #cython: language_level=3 # distutils: language = c++ # distutils: sources = Rectangle.cpp cdef extern from "Rectangle.h" namespace "shapes": cdef cppclass Rectangle: Rectangle() except + Rectangle(int, int, int, int) except + int x0, y0, x1, y1 int getArea() void getSize(int* width, int* height) void move(int, int) @staticmethod int do_something() cdef class PyRectangle: cdef Rectangle c_rect # hold a C++ instance which we're wrapping def __cinit__(self, int x0, int y0, int x1, int y1): self.c_rect = Rectangle(x0, y0, x1, y1) def get_area(self): return self.c_rect.getArea() def get_size(self): cdef int width, height self.c_rect.getSize(&width, &height) return width, height def move(self, dx, dy): self.c_rect.move(dx, dy) @staticmethod def do_something(): return Rectangle.do_something()
4
%%writefile rect.pyx #cython: language_level=3 # distutils: language = c++ # distutils: sources = Rectangle.cpp cdef extern from "Rectangle.h" namespace "shapes": cdef cppclass Rectangle: Rectangle() except + Rectangle(int, int, int, int) except + int x0, y0, x1, y1 int getArea() void getSize(int* width, int* height) void move(int, int) @staticmethod int do_something() cdef class PyRectangle: cdef Rectangle c_rect # hold a C++ instance which we're wrapping def __cinit__(self, int x0, int y0, int x1, int y1): self.c_rect = Rectangle(x0, y0, x1, y1) def get_area(self): return self.c_rect.getArea() def get_size(self): cdef int width, height self.c_rect.getSize(&width, &height) return width, height def move(self, dx, dy): self.c_rect.move(dx, dy) @staticmethod def do_something(): return Rectangle.do_something()
5
1.11. 模板
Cython使用括号语法进行模板化。下面是一个包装C ++ Vector的简单示例
%%writefile rect.pyx #cython: language_level=3 # distutils: language = c++ # distutils: sources = Rectangle.cpp cdef extern from "Rectangle.h" namespace "shapes": cdef cppclass Rectangle: Rectangle() except + Rectangle(int, int, int, int) except + int x0, y0, x1, y1 int getArea() void getSize(int* width, int* height) void move(int, int) @staticmethod int do_something() cdef class PyRectangle: cdef Rectangle c_rect # hold a C++ instance which we're wrapping def __cinit__(self, int x0, int y0, int x1, int y1): self.c_rect = Rectangle(x0, y0, x1, y1) def get_area(self): return self.c_rect.getArea() def get_size(self): cdef int width, height self.c_rect.getSize(&width, &height) return width, height def move(self, dx, dy): self.c_rect.move(dx, dy) @staticmethod def do_something(): return Rectangle.do_something()
6
%%writefile rect.pyx #cython: language_level=3 # distutils: language = c++ # distutils: sources = Rectangle.cpp cdef extern from "Rectangle.h" namespace "shapes": cdef cppclass Rectangle: Rectangle() except + Rectangle(int, int, int, int) except + int x0, y0, x1, y1 int getArea() void getSize(int* width, int* height) void move(int, int) @staticmethod int do_something() cdef class PyRectangle: cdef Rectangle c_rect # hold a C++ instance which we're wrapping def __cinit__(self, int x0, int y0, int x1, int y1): self.c_rect = Rectangle(x0, y0, x1, y1) def get_area(self): return self.c_rect.getArea() def get_size(self): cdef int width, height self.c_rect.getSize(&width, &height) return width, height def move(self, dx, dy): self.c_rect.move(dx, dy) @staticmethod def do_something(): return Rectangle.do_something()
7
多个模板参数可以定义为列表,如[T,U,V]
或[int,bool,char]
.可以通过写入[T,U,V = *]
来指示可选的模板参数.
如果Cython需要显式引用不完整模板实例化的默认模板参数的类型,它将编写MyClass <T,U> :: V
,所以如果类为其模板参数提供了typedef
,那么最好在这里使用该名称.
模板函数的定义与类模板类似,模板参数列表跟随函数名称:
%%writefile rect.pyx #cython: language_level=3 # distutils: language = c++ # distutils: sources = Rectangle.cpp cdef extern from "Rectangle.h" namespace "shapes": cdef cppclass Rectangle: Rectangle() except + Rectangle(int, int, int, int) except + int x0, y0, x1, y1 int getArea() void getSize(int* width, int* height) void move(int, int) @staticmethod int do_something() cdef class PyRectangle: cdef Rectangle c_rect # hold a C++ instance which we're wrapping def __cinit__(self, int x0, int y0, int x1, int y1): self.c_rect = Rectangle(x0, y0, x1, y1) def get_area(self): return self.c_rect.getArea() def get_size(self): cdef int width, height self.c_rect.getSize(&width, &height) return width, height def move(self, dx, dy): self.c_rect.move(dx, dy) @staticmethod def do_something(): return Rectangle.do_something()
8
%%writefile rect.pyx #cython: language_level=3 # distutils: language = c++ # distutils: sources = Rectangle.cpp cdef extern from "Rectangle.h" namespace "shapes": cdef cppclass Rectangle: Rectangle() except + Rectangle(int, int, int, int) except + int x0, y0, x1, y1 int getArea() void getSize(int* width, int* height) void move(int, int) @staticmethod int do_something() cdef class PyRectangle: cdef Rectangle c_rect # hold a C++ instance which we're wrapping def __cinit__(self, int x0, int y0, int x1, int y1): self.c_rect = Rectangle(x0, y0, x1, y1) def get_area(self): return self.c_rect.getArea() def get_size(self): cdef int width, height self.c_rect.getSize(&width, &height) return width, height def move(self, dx, dy): self.c_rect.move(dx, dy) @staticmethod def do_something(): return Rectangle.do_something()
9
1.11.1. 使用默认构造函数简化包装
如果扩展类型使用默认构造函数(不传递任何参数)来实例化包装的C++类,则可以通过将其直接绑定到Python包装器对象的生命周期来简化生命周期处理。取代声明一个指针,我们可以声明一个实例
Overwriting rect.pyx
0
Overwriting rect.pyx
1
Overwriting rect.pyx
2
Overwriting rect.pyx
3
Overwriting rect.pyx
4
Overwriting rect.pyx
5
Overwriting rect.pyx
4
Overwriting rect.pyx
7
当Python对象被创建时,Cython将自动生成实例化C ++对象实例的代码,并在Python对象被垃圾回收时将其删除。
1.11.2. 异常Exception处理
Cython不能抛出C++异常,或者使用try-except语句来捕获它们,但是有可能通过在声明函数时在其后加上except +
来声明一个函数可能引发C++异常并将其转换为Python异常.例如长方体例子中的
Overwriting rect.pyx
8
这将将try和C++错误翻译成适当的Python异常。根据下表执行翻译(C++标识符中省略了std ::前缀):
C++异常 | Python异常 |
---|---|
bad_alloc | MemoryError |
bad_cast | TypeError |
bad_typeid | TypeError |
domain_error | ValueError |
invalid_argument | ValueError |
ios_base::failure | IOError |
out_of_range | IndexError |
overflow_error | OverflowError |
range_error | ArithmeticError |
underflow_error | ArithmeticError |
(all others) | RuntimeError |
如果except +
后面加上指定的python错误类型,则会将捕获到的C++异常转化为指定的python错误
Overwriting rect.pyx
9
就会指定bar()函数报错后转化为MemoryError
同时也可以通过实现一个函数来指定捕获的错误转化为何种python异常
%%writefile setup.py from distutils.core import setup from Cython.Build import cythonize setup( name = "rectangleapp", ext_modules = cythonize('*.pyx') )
0
如果有不可预知的错误代码引发了一个C++异常,那么raise_py_error
将被调用,这允许一个人自定义C++
到Python的错误“translations”.如果raise_py_error
实际上并不引发一个异常,则会引发一个RuntimeError
.
还没有评论,来说两句吧...