构造Python绑定到C或c++库的最快方法是什么?

(如果这很重要的话,我使用的是Windows。)


当前回答

还有pybind11,它类似于Boost的轻量级版本。Python和兼容所有现代c++编译器:

https://pybind11.readthedocs.io/en/latest/

其他回答

我喜欢cppyy,它使得用c++代码扩展Python变得非常容易,在需要时极大地提高了性能。

它功能强大,坦白说使用起来非常简单,

这里是一个如何创建numpy数组并将其传递给c++中的类成员函数的示例。

cppyy_test.py

import cppyy
import numpy as np
cppyy.include('Buffer.h')


s = cppyy.gbl.Buffer()
numpy_array = np.empty(32000, np.float64)
s.get_numpy_array(numpy_array.data, numpy_array.size)
print(numpy_array[:20])

Buffer.h

struct Buffer {
  void get_numpy_array(double *ad, int size) {
    for( long i=0; i < size; i++)
        ad[i]=i;
  }
};

你也可以很容易地创建一个Python模块(使用CMake),这样你就可以避免一直重新编译c++代码。

还有pybind11,它类似于Boost的轻量级版本。Python和兼容所有现代c++编译器:

https://pybind11.readthedocs.io/en/latest/

首先你应该确定你的特殊目的是什么。上面提到了关于扩展和嵌入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

问题是如何从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

关于详细的指南,你可以参考我的博客文章。