【Cython】用Cython包装C++代码,提供给python调用

来源:互联网 发布:怎样才能约到炮 知乎 编辑:程序博客网 时间:2024/05/16 19:28

【核心概要】

(1) 定义.h和.cpp文件(C++头文件和源文件);

(2) 定义.pyx文件(Cython文件);

  • 声明Cython中的C++类接口,包装C++类:cdef extern from "Rectangle.h" namespace "shapes":
  • 声明Cython类,用于使用C++类:cdef cppclass Rectangle:
  • 处理结构体:Rectangle(int, int, int, int) except +,except +让Cython能够识别并容纳C++的错误;
  • 声明属性和变量:int x0, y0, x1, y1;  int getArea(); void getSize(int* width, int* height); void move(int, int);
  • 用C++包装类声明变量:rec_ptr = new Rectangle(1, 2, 3, 4);
  • 创建Cython包装函数:cdef class PyRectangle:# 定义python函数,返回cython调用的C++函数计算结果;

(3) 编译.pyx和.cpp\.h文件,链接到一起(通过Setup.py文件);

(4) 定义并运行.py文件(python文件),得到计算结果;

官方案例地址:https://cython.readthedocs.io/en/latest/src/userguide/wrapping_CPlusPlus.html

国内下载地址:http://download.csdn.net/download/yuan8222/9789553

============================================================================

1.Python再包装

在 Python 程序中,是看不到 cdef 的函数的,所以我们这里 def naive_dot(a, b) 来调用 cdef 过的 _naive_dot 函数。

2.编译语句-运行

python setup.py build_ext --inplace

3.编译函数-参数解释

以下代码为举例说明

# setup.py from distutils.core import setup, Extensionfrom Cython.Build import cythonizeimport numpysetup(ext_modules = cythonize(Extension('dot_cython',sources=['libtest.pyx'],language='c++',include_dirs=[numpy.get_include()],library_dirs=[],libraries=[],extra_compile_args=[],extra_link_args=[])))

'dot_cython' 是我们要生成的动态链接库的名字sources 里面可以包含 .pyx 文件,以及后面如果我们要调用 C/C++ 程序的话,还可以往里面加 .c / .cpp 文件language 其实默认就是 c,如果要用 C++,就改成 c++ 就好了include_dirs 这个就是传给 gcc 的 -I 参数library_dirs 这个就是传给 gcc 的 -L 参数libraries 这个就是传给 gcc 的 -l 参数extra_compile_args 就是传给 gcc 的额外的编译参数,比方说你可以传一个 -std=c++11extra_link_args 就是传给 gcc 的额外的链接参数(也就是生成动态链接库的时候用的)如果你从来没见过上面几个 gcc 参数,说明你暂时还没这些需求,等你遇到了你就懂了

检查优化地方: cython -a dot_cython.pyx

4.编译函数-用Cython包装C++

Rectangle.cpp文件:

#include "Rectangle.h"Rectangle::Rectangle() { }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;}
setup.py文件:

from distutils.core import setupfrom Cython.Build import cythonizesetup(ext_modules = cythonize(   "rect.pyx",                 # our Cython source   sources=["Rectangle.cpp"],  # additional source file(s)   language="c++",             # generate C++ code))

5.Python调用

rect_test.py:

import rectif name == 'main':pyRect = rect.PyRectangle(100, 100, 300, 500)width, height = pyRect.get_size()print("size: width = %d, height = %d" % (width, height))

具体代码见:http://download.csdn.net/download/yuan8222/9789553

6. 运行失败时候读这里

第四步中的setup.py楼主在python3.5+cython0.26环境下没有运行成功,运行成功的代码为:

from distutils.core import setup, Extensionfrom Cython.Build import cythonizesetup(ext_modules = cythonize(Extension(           "rect",                                # the extension name           sources=["rect.pyx", "Rectangle.cpp"], # the Cython source and                                                  # additional C++ source files           language="c++",                        # generate and compile C++ code      )))
主要参考下面的内容:

The best way to build Cython code from setup.py scripts is the cythonize() function. To make Cython generate and compile C++ code with distutils, you just need to pass the option language="c++":

from distutils.core import setupfrom Cython.Build import cythonizesetup(ext_modules = cythonize(           "rect.pyx",                 # our Cython source           sources=["Rectangle.cpp"],  # additional source file(s)           language="c++",             # generate C++ code      ))

Cython will generate and compile the rect.cpp file (from the rect.pyx), then it will compile Rectangle.cpp (implementation of the Rectangle class) and link both objects files together into rect.so, which you can then import in Python using import rect (if you forget to link the Rectangle.o, you will get missing symbols while importing the library in Python).

Note that the language option has no effect on user provided Extension objects that are passed into cythonize(). It is only used for modules found by file name (as in the example above).

The cythonize() function in Cython versions up to 0.21 does not recognize the language option and it needs to be specified as an option to an Extension that describes your extension and that is then handled by cythonize() as follows:

from distutils.core import setup, Extensionfrom Cython.Build import cythonizesetup(ext_modules = cythonize(Extension(           "rect",                                # the extension name           sources=["rect.pyx", "Rectangle.cpp"], # the Cython source and                                                  # additional C++ source files           language="c++",                        # generate and compile C++ code      )))

The options can also be passed directly from the source file, which is often preferable (and overrides any global option). Starting with version 0.17, Cython also allows to pass external source files into the cythonize() command this way. Here is a simplified setup.py file:

from distutils.core import setupfrom Cython.Build import cythonizesetup(    name = "rectangleapp",    ext_modules = cythonize('*.pyx'),)

And in the .pyx source file, write this into the first comment block, before any source code, to compile it in C++ mode and link it statically against the Rectangle.cpp code file:

# distutils: language = c++# distutils: sources = Rectangle.cpp

To compile manually (e.g. using make), the cython command-line utility can be used to generate a C++ .cpp file, and then compile it into a python extension. C++ mode for the cython command is turned on with the --cplus option.

原创粉丝点击