从Python调用C / C ++?

断了今生、忘了曾经 提交于 2020-01-18 07:32:50

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

(如果这很重要,我正在使用Windows。)


#1楼

最快的方法是使用SWIG

来自SWIG 教程的示例

/* File : example.c */
int fact(int n) {
    if (n <= 1) return 1;
    else return n*fact(n-1);
}

接口文件:

/* example.i */
%module example
%{
/* Put header files here or function declarations like below */
extern int fact(int n);
%}

extern int fact(int n);

在Unix上构建Python模块:

swig -python example.i
gcc -fPIC -c example.c example_wrap.c -I/usr/local/include/python2.7
gcc -shared example.o example_wrap.o -o _example.so

用法:

>>> import example
>>> example.fact(5)
120

请注意,您必须具有python-dev。 同样在某些系统中,python头文件会根据您的安装方式位于/usr/include/python2.7中。

从教程中:

SWIG是一个相当完整的C ++编译器,几乎支持所有语言功能。 这包括预处理,指针,类,继承,甚至C ++模板。 SWIG还可以用于以目标语言将结构和类打包到代理类中-以非常自然的方式公开基础功能。


#2楼

我从未使用过它,但是我听说过有关ctypes的好东西。 如果您尝试将其与C ++一起使用,请确保通过extern "C"逃避名称处理。 感谢弗洛里安·博斯(FlorianBösch)的评论。


#3楼

本文声称Python是科学家的全部需要 ,基本上说:首先用Python制作一切原型。 然后,当您需要加快零件速度时,请使用SWIG并将其转换为C。


#4楼

一份正式的Python文档包含有关使用C / C ++扩展Python的详细信息。 即使不使用SWIG ,它也非常简单,并且在Windows上也能很好地工作。


#5楼

检查出pyrexCython 。 它们是类似于Python的语言,用于C / C ++和Python之间的接口。


#6楼

您应该看看Boost.Python 。 以下是他们网站上的简短介绍:

Boost Python库是用于连接Python和C ++的框架。 它使您无需使用特殊工具(仅使用C ++编译器)即可快速,无缝地向Python展示C ++类的函数和对象,反之亦然。 它被设计为以非侵入方式包装C ++接口,因此您不必为了包装而完全更改C ++代码,从而使Boost.Python成为将第三方库公开给Python的理想选择。 该库对高级元编程技术的使用为用户简化了其语法,因此包装代码具有一种声明性接口定义语言(IDL)的外观。


#7楼

ctypes是标准库的一部分,因此比swig更稳定和更易于使用,后者经常会给我带来麻烦

使用ctypes时,您需要满足对python的任何编译时依赖性,并且绑定将对任何具有ctypes的python起作用,而不仅仅是针对ctypes的python。

假设您要在一个名为foo.cpp的文件中讨论一个简单的C ++示例类:

#include <iostream>

class Foo{
    public:
        void bar(){
            std::cout << "Hello" << std::endl;
        }
};

由于ctypes只能与C函数对话,因此您需要提供将其声明为extern“ C”的那些函数

extern "C" {
    Foo* Foo_new(){ return new Foo(); }
    void Foo_bar(Foo* foo){ foo->bar(); }
}

接下来,您必须将其编译为共享库

g++ -c -fPIC foo.cpp -o foo.o
g++ -shared -Wl,-soname,libfoo.so -o libfoo.so  foo.o

最后,您必须编写python包装器(例如,在fooWrapper.py中)

from ctypes import cdll
lib = cdll.LoadLibrary('./libfoo.so')

class Foo(object):
    def __init__(self):
        self.obj = lib.Foo_new()

    def bar(self):
        lib.Foo_bar(self.obj)

一旦有了,您可以像这样称呼它

f = Foo()
f.bar() #and you will see "Hello" on the screen

#8楼

我认为cffi for python是一个选择。

目的是从Python调用C代码。 您应该能够在不学习第三语言的情况下进行操作:每种选择都要求您学习自己的语言(Cython,SWIG)或API(ctypes)。 因此,我们尝试假设您了解Python和C,并尽量减少了您需要学习的API附加位。

http://cffi.readthedocs.org/en/release-0.7/


#9楼

我从此页面的Python <-> C ++绑定开始了我的旅程,目的是链接高级数据类型(带有Python列表的多维STL向量):-)

尝试过基于ctypesboost.python的解决方案(并且不是软件工程师),当需要高级数据类型绑定时,我发现它们很复杂,而对于这种情况,我发现SWIG更加简单。

因此,该示例使用了SWIG,并且已经在Linux中进行了测试(但是SWIG可用,并且在Windows中也被广泛使用)。

目的是使C ++函数可用于Python,该函数采用2D STL向量形式的矩阵并返回每一行的平均值(作为1D 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

#10楼

首先,您应该确定自己的特定目的。 上面提到有关扩展和嵌入Python解释器的官方Python文档,我可以添加一个很好的二进制扩展概述 。 用例可分为3类:

  • 加速器模块 :运行速度比CPython中运行的等效纯Python代码更快。
  • 包装模块 :将现有的C接口公开给Python代码。
  • 低级系统访问 :访问CPython运行时,操作系统或底层硬件的低级功能。

为了给其他感兴趣的人一个更广阔的视野,并且由于您的最初问题有点含糊(“对C或C ++库”),我想您可能会对这些信息感兴趣。 在上面的链接上,您可以了解使用二进制扩展名及其替代方法的缺点。

除了建议的其他答案外,如果您需要加速器模块,还可以尝试Numba 。 它的工作原理是“通过在导入时,运行时或静态(使用附带的pycc工具)使用LLVM编译器基础结构生成优化的机器代码”。


#11楼

问题是,如果我理解正确的话,如何从Python调用C函数。 然后最好的选择是Ctypes(BTW可在所有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

有关详细指南,您可能需要参考我的博客文章


#12楼

除非您期望编写Java包装程序,否则Cython绝对是必经之路,在这种情况下,SWIG可能更可取。

我建议使用runcython命令行实用程序,它使使用Cython的过程非常容易。 如果您需要将结构化数据传递给C ++,请查看Google的protobuf库,它非常方便。

这是我使用这两种工具的最小示例:

https://github.com/nicodjimenez/python2cpp

希望它可以是一个有用的起点。


#13楼

还有pybind11 ,它像Boost.Python的轻量级版本,并且与所有现代C ++编译器兼容:

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


#14楼

对于现代C ++,请使用cppyy: http ://cppyy.readthedocs.io/en/latest/

它基于Cling / Clang / LLVM的C ++解释器。 绑定是在运行时执行的,不需要其他中间语言。 感谢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的作者。


#15楼

您可以使用Scapix语言桥直接从C ++标头自动生成Python绑定,作为构建的一部分。

scapix_bridge_headers()的调用添加到CMakeLists.txt文件,然后使用cmake -DSCAPIX_BRIDGE=python构建项目。 查看完整示例

免责声明:我是Scapix Language Bridge的作者。

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!