首页 文章

从Python调用C / C?

提问于
浏览
418

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

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

14 回答

  • 7

    Cython肯定是要走的路,除非你期望编写Java包装器,在这种情况下,SWIG可能更好 .

    我建议使用 runcython 命令行实用程序,这使得使用Cython的过程非常简单 . 如果您需要将结构化数据传递给C,请查看Google 's protobuf library, it'非常方便 .

    以下是我使用这两种工具制作的最小示例:

    https://github.com/nicodjimenez/python2cpp

    希望它可以成为一个有用的起点 .

  • 14

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

    Boost Python Library是一个用于连接Python和C的框架 . 它允许您快速无缝地将C类函数和对象公开给Python,反之亦然,不使用特殊工具 - 只需使用C编译器 . 它旨在非侵入性地包装C接口,因此您不必更改C代码以包装它,使Boost.Python成为将第三方库暴露给Python的理想选择 . 该库使用高级元编程技术简化了用户的语法,因此包装代码呈现出一种声明性接口定义语言(IDL) .

  • 10

    ctypes是标准库的一部分,因此比swig更稳定和广泛可用,它总是倾向于给我problems .

    使用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
    
  • 47

    最快的方法是使用SWIG .

    SWIG tutorial中的示例:

    /* 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还可用于将结构和类打包到目标语言的代理类中 - 以非常自然的方式公开底层功能 .

  • 16

    我从这个页面开始我的Python < - > C绑定之旅,目的是链接高级数据类型(多维STL向量与Python列表):-)

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

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

    目标是使Python可以使用C函数,该函数采用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;
    }
    

    等效 Headers (“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 interface definition file("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
    
  • 6

    查看pyrexCython . 它们是类似Python的语言,用于连接C / C和Python .

  • 4

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

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

  • 4

    This paper, claiming Python to be all a scientist needs,基本上说:首先用Python编写原型 . 然后,当您需要加速部分时,使用SWIG并将此部分转换为C.

  • 134

    我从来没有用过它,但我听说过关于ctypes的好消息 . 如果您尝试将其与C一起使用,请务必通过extern "C"来避免名称损坏 . 感谢您的评论,FlorianBösch .

  • 40

    我认为python的cffi可以是一个选项 .

    目标是从Python调用C代码 . 你应该能够在不学习第三语言的情况下这样做:每个选择都需要你学习他们自己的语言(Cython,SWIG)或API(ctypes) . 因此,我们尝试假设您了解Python和C,并最大限度地减少您需要学习的额外API .

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

  • 25

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

    它基于Cling,Clang / LLVM的C解释器 . 绑定在运行时,不需要额外的中间语言 . 感谢Clang,它支持C 17 .

    使用pip安装它:

    $ pip install cppyy
    

    对于小型项目,只需加载相关的库和您感兴趣的 Headers . 例如 . 从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

    其中一个官方Python文档包含extending Python using C/C++的详细信息 . 即使不使用SWIG,它也非常简单,在Windows上运行得非常好 .

  • 523

    问题是如果我理解正确的话,如何从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
    

    有关详细指南,您可能需要参考my blog article .

  • 3

    首先,你应该决定你的特定目的是什么 . 上面提到了extending and embedding the Python interpreter上的官方Python文档,我可以添加一个好的overview of binary extensions . 用例可分为3类:

    • accelerator modules :比CPython中运行的等效纯Python代码运行得更快 .

    • wrapper modules :将现有C接口暴露给Python代码 .

    • low level system access :访问CPython运行时,操作系统或底层硬件的低级功能 .

    为了给其他感兴趣的人提供一些更广泛的视角,因为你的初始问题有点模糊(“到C或C库”),我认为这些信息对你来说可能很有趣 . 在上面的链接中,您可以阅读使用二进制扩展及其替代方案的缺点 .

    除了建议的其他答案,如果你想要一个加速器模块,你可以试试Numba . 它工作"by generating optimized machine code using the LLVM compiler infrastructure at import time, runtime, or statically (using the included pycc tool)" .

相关问题