构造Python绑定到C或c++库的最快方法是什么?
(如果这很重要的话,我使用的是Windows。)
构造Python绑定到C或c++库的最快方法是什么?
(如果这很重要的话,我使用的是Windows。)
当前回答
首先你应该确定你的特殊目的是什么。上面提到了关于扩展和嵌入Python解释器的官方Python文档,我可以添加一个很好的二进制扩展概述。用例可以分为3类:
加速器模块:比在CPython中运行的等效纯Python代码运行得更快。 包装器模块:将现有的C接口公开给Python代码。 低级系统访问:访问CPython运行时、操作系统或底层硬件的低级特性。
为了给其他感兴趣的人提供一些更广阔的视角,而且因为你最初的问题有点模糊(“到C或c++库”),我认为这些信息可能会让你感兴趣。在上面的链接中,您可以阅读到使用二进制扩展及其替代方案的缺点。
除了建议的其他答案外,如果您想要一个加速模块,您可以尝试Numba。它的工作原理是“在导入时、运行时或静态(使用包含的pycc工具)使用LLVM编译器基础设施生成优化的机器代码”。
其他回答
问题是如何从Python中调用C函数,如果我理解正确的话。那么最好的选择是c类型(顺便说一句,可移植到所有Python变体)。
>>> from ctypes import *
>>> libc = cdll.msvcrt
>>> print libc.time(None)
1438069008
>>> printf = libc.printf
>>> printf("Hello, %s\n", "World!")
Hello, World!
14
>>> printf("%d bottles of beer\n", 42)
42 bottles of beer
19
关于详细的指南,你可以参考我的博客文章。
Cython绝对是最好的选择,除非您希望编写Java包装器,在这种情况下,SWIG可能更可取。
我建议使用runcython命令行实用工具,它使使用Cython的过程极其简单。如果你需要向c++传递结构化数据,可以看看谷歌的protobuf库,它非常方便。
以下是我使用这两种工具的一个最小示例:
https://github.com/nicodjimenez/python2cpp
希望这能成为一个有用的起点。
对于现代c++,使用cppyy: http://cppyy.readthedocs.io/en/latest/
它基于Clang/LLVM的c++解释器kling。绑定是在运行时进行的,不需要额外的中间语言。得益于Clang,它支持c++ 17。
使用pip安装:
$ pip install cppyy
对于小型项目,只需加载您感兴趣的相关库和标头。例如,从ctypes例子中获取的代码是这个线程,但是在头和代码部分中被分割:
$ cat foo.h
class Foo {
public:
void bar();
};
$ cat foo.cpp
#include "foo.h"
#include <iostream>
void Foo::bar() { std::cout << "Hello" << std::endl; }
编译:
$ g++ -c -fPIC foo.cpp -o foo.o
$ g++ -shared -Wl,-soname,libfoo.so -o libfoo.so foo.o
并使用它:
$ python
>>> import cppyy
>>> cppyy.include("foo.h")
>>> cppyy.load_library("foo")
>>> from cppyy.gbl import Foo
>>> f = Foo()
>>> f.bar()
Hello
>>>
大型项目支持自动加载准备好的反射信息和cmake片段来创建它们,以便已安装包的用户可以简单地运行:
$ python
>>> import cppyy
>>> f = cppyy.gbl.Foo()
>>> f.bar()
Hello
>>>
多亏了LLVM,高级特性才得以实现,比如自动模板实例化。继续这个例子:
>>> v = cppyy.gbl.std.vector[cppyy.gbl.Foo]()
>>> v.push_back(f)
>>> len(v)
1
>>> v[0].bar()
Hello
>>>
注:我是cppyy的作者。
首先你应该确定你的特殊目的是什么。上面提到了关于扩展和嵌入Python解释器的官方Python文档,我可以添加一个很好的二进制扩展概述。用例可以分为3类:
加速器模块:比在CPython中运行的等效纯Python代码运行得更快。 包装器模块:将现有的C接口公开给Python代码。 低级系统访问:访问CPython运行时、操作系统或底层硬件的低级特性。
为了给其他感兴趣的人提供一些更广阔的视角,而且因为你最初的问题有点模糊(“到C或c++库”),我认为这些信息可能会让你感兴趣。在上面的链接中,您可以阅读到使用二进制扩展及其替代方案的缺点。
除了建议的其他答案外,如果您想要一个加速模块,您可以尝试Numba。它的工作原理是“在导入时、运行时或静态(使用包含的pycc工具)使用LLVM编译器基础设施生成优化的机器代码”。
我从本页开始了我的Python <-> c++绑定之旅,目标是链接高级数据类型(多维STL向量与Python列表):-)
已经尝试了基于ctypes和boost的解决方案。当需要高级数据类型绑定时,我发现它们很复杂,而在这种情况下,我发现SWIG要简单得多。
因此,本例使用SWIG,并且已经在Linux中进行了测试(但SWIG是可用的,并且在Windows中也广泛使用)。
目标是为Python提供一个c++函数,该函数接受二维STL向量形式的矩阵,并返回每一行的平均值(作为一维STL向量)。
c++中的代码("code.cpp")如下:
#include <vector>
#include "code.h"
using namespace std;
vector<double> average (vector< vector<double> > i_matrix) {
// Compute average of each row..
vector <double> averages;
for (int r = 0; r < i_matrix.size(); r++){
double rsum = 0.0;
double ncols= i_matrix[r].size();
for (int c = 0; c< i_matrix[r].size(); c++){
rsum += i_matrix[r][c];
}
averages.push_back(rsum/ncols);
}
return averages;
}
等价的头文件("code.h")是:
#ifndef _code
#define _code
#include <vector>
std::vector<double> average (std::vector< std::vector<double> > i_matrix);
#endif
我们首先编译c++代码来创建一个目标文件:
g++ -c -fPIC code.cpp
然后,我们为c++函数定义一个SWIG接口定义文件(“code.i”)。
%module code
%{
#include "code.h"
%}
%include "std_vector.i"
namespace std {
/* On a side note, the names VecDouble and VecVecdouble can be changed, but the order of first the inner vector matters! */
%template(VecDouble) vector<double>;
%template(VecVecdouble) vector< vector<double> >;
}
%include "code.h"
使用SWIG,我们从SWIG接口定义文件中生成一个c++接口源代码。
swig -c++ -python code.i
我们最终编译生成的c++接口源文件,并将所有内容链接在一起,以生成一个可以由Python直接导入的共享库(“_”很重要):
g++ -c -fPIC code_wrap.cxx -I/usr/include/python2.7 -I/usr/lib/python2.7
g++ -shared -Wl,-soname,_code.so -o _code.so code.o code_wrap.o
我们现在可以在Python脚本中使用该函数:
#!/usr/bin/env python
import code
a= [[3,5,7],[8,10,12]]
print a
b = code.average(a)
print "Assignment done"
print a
print b