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);     }; } 71.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);     }; } 8Cython包装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.h0- 注意:你只能在一个cdef extern从块里面这样做;任何其他地方的struct声明必须是非空的。 
- 如果头文件使用 - typedef名称(如word)来引用与平台相关的数值类型的风格,则需要一个相应的- ctypedef语句,但不需要完全匹配类型,只是使用一些正确的一般类型(int,float等). 例如:- Overwriting Rectangle.h1- 将工作正常无论实际大小的单词是(提供的头文件正确定义它).与Python类型(如果有)之间的转换也将用于此新类型. 
- 如果头文件使用宏来定义常量,则将它们转换为正常的外部变量声明。如果它们包含正常的int值,也可以将它们声明为枚举。请注意,Cython认为枚举等同于int,因此不要对非int值执行此操作. 
- 如果头文件使用宏定义了一个函数,那么声明它就像是一个普通的函数,具有适当的参数和结果类型 - 如果你想包含一个C头,因为它是另一个头需要的,但不想使用它的任何声明,在extern-from块中放入pass关键字: - Overwriting Rectangle.h2- 如果要包括系统标题,请在引号中加上尖括号: - Overwriting Rectangle.h3- 如果你想包含一些外部声明,但不想指定一个头文件(因为它包含了你已经包含的其他头文件),你可以用*代替头文件名: - Overwriting Rectangle.h4
1.7. 在C/C++中实现
另一种简单的写法是直接使用外部声明声明C/C++实现
Overwriting Rectangle.h 5Overwriting Rectangle.h 6Overwriting Rectangle.h 7Overwriting Rectangle.h 8Overwriting Rectangle.h 9Overwriting 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;     }  } 9Overwriting Rectangle.cpp 0Overwriting Rectangle.cpp 1Overwriting Rectangle.cpp 2Overwriting Rectangle.cpp 3Overwriting Rectangle.cpp 4Overwriting Rectangle.cpp 5Overwriting Rectangle.cpp 6Overwriting Rectangle.cpp 7Overwriting 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() 51.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() 91.11.1. 使用默认构造函数简化包装
如果扩展类型使用默认构造函数(不传递任何参数)来实例化包装的C++类,则可以通过将其直接绑定到Python包装器对象的生命周期来简化生命周期处理。取代声明一个指针,我们可以声明一个实例
Overwriting rect.pyx 0Overwriting rect.pyx 1Overwriting rect.pyx 2Overwriting rect.pyx 3Overwriting rect.pyx 4Overwriting rect.pyx 5Overwriting rect.pyx 4Overwriting 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.




 
		 
		 
		 
		

还没有评论,来说两句吧...