C/C++ 提供 Python 接口

栏目: C++ · 发布时间: 5年前

内容简介:Tensorflow 的核心代码是 C 和 C++ 实现然后提供 Python 的接口,使用者无需关心 C++ 的细节,直接使用 Python 就能调用相关功能,而且这些功能多是比较复杂且耗时的操作。类似的有 caffe , xgboost 的工具包。C++ 和 Python 之间的交互有很多种,而且在不同场景下可以选择不同的方式。这篇文章列举一些常用的方式,并给出简单的例子方便理解和选择不同方式来解决自己的问题。Python 官方文档介绍了直接在 C 中调用 CPython

C/C++ 和 Python 之间的交互

Tensorflow 的核心代码是 C 和 C++ 实现然后提供 Python 的接口,使用者无需关心 C++ 的细节,直接使用 Python 就能调用相关功能,而且这些功能多是比较复杂且耗时的操作。类似的有 caffe , xgboost 的 工具 包。

C++ 和 Python 之间的交互有很多种,而且在不同场景下可以选择不同的方式。这篇文章列举一些常用的方式,并给出简单的例子方便理解和选择不同方式来解决自己的问题。

直接调用 CPython 的接口

Python 官方文档介绍了直接在 C 中调用 CPython 接口 的方式来实现 Python 扩展。这需要了解 Python 提供的接口,然后利用 distutils/setuptools 来构建扩展 如下面的例子,C 代码是现在 hello.c 文件中,然后写 setup.py 运行 python setup.py build_ext —inplace 然后就可以在当前目录下直接 import hello

include <stdio.h>
#include <Python.h>

static PyObject* hello_world(PyObject *self, PyObject *args) {
    printf("Hello, world!\n");
    Py_RETURN_NONE;
}

static PyObject* hello(PyObject *self, PyObject *args) {
    const char* name;
    if (!PyArg_ParseTuple(args, "s", &name)) {
        return NULL;
    }

    printf("Hello, %s!\n", name);
    Py_RETURN_NONE;
}

static PyMethodDef hello_methods[] = {
    {
        "hello_world", hello_world, METH_NOARGS,
        "Print 'hello world' from a method defined in a C extension."
    },
    {
        "hello", hello, METH_VARARGS,
        "Print 'hello xxx' from a method defined in a C extension."
    },
    {NULL, NULL, 0, NULL}
};

static struct PyModuleDef hello_definition = {
    PyModuleDef_HEAD_INIT,
    "hello",
    "A Python module that prints 'hello world' from C code.",
    -1,
    hello_methods
};

PyMODINIT_FUNC PyInit_hello(void) {
    Py_Initialize();
    return PyModule_Create(&hello_definition);
}
# setup.py
from distutils.core import setup, Extension

hello_module = Extension('hello', sources = ['hello.c'])

setup(name='hello',
      version='0.1.0',
      description='Hello world module written in C',
      ext_modules=[hello_module])

官放还提供了一种方式直接把整个 CPython 嵌入到项目里,有需要可以查看 详细文档

Cython

按官方的实现方式我们需要按照 C/C++ 去实现功能, Cython 的想法是用 Python 本身的语法去实现功能,然后 Cython 会翻译 对应的 .c 文件,然后按照上面的方式利用 distutils/setuptools 来构建扩展。Cython 的实现一般放在 .pyx 的后缀文件中。下面还是一个例子.

hello.pyx

def say_hello_to(name):
    print("Hello %s!" % name)

setup.py

rom distutils.core import setup
from Cython.Build import cythonize

setup(name='Hello world app',
      ext_modules=cythonize("hello.pyx"))

在当前目录执行 python setup.py build_ext —inplace 可以得到动态对应的 .c 文件和编译好的动态链接库。

在当前目录可以直接import 当然你可以 python setup.py install 来安装到系统下。

ctypes

这是在 Python 官方标准库中实现的包,相对于直接调用 CPython 的接口的优雅之处在于,我们只需要写 C 代码然后剩下的交给 ctypes 去处理接好了,我们不需要去花时间了解 CPython 的接口。

hello.c

#include <stdio.h>

void hello(void);

void hello()
{
    printf("hello world");
}

编译: gcc -shared -Wl,-install_name,hello.so -o hello.so -fPIC hello.c

然后就可以在 ```hello.py`` 中直接调用

hello.py

import ctypes

hello = ctypes.CDLL('hello.so')

hello.hello()

类似的还有 cffi

swig

swig 强大之处在于可以把 C/C++ 写的东西和十几种其他语言进行交互,这其中就包括 Python 。但是也需要我们写一个 .i 后缀的接口文件。swig 有优势也有很多不足, Stack Overflow 的比较 ctypes 和 swig 的问题下一答者记录了他在使用 Swig 不同时间段的不同感受. Tensorflow 的实现方式就是利用了 swig 来实现的。下面是一个官方的例子.

example.c

#include <time.h>
 double My_variable = 3.0;
 
 int fact(int n) {
     if (n <= 1) return 1;
     else return n*fact(n-1);
 }
 
 int my_mod(int x, int y) {
     return (x%y);
 }
 	
 char *get_time()
 {
     time_t ltime;
     time(&ltime);
     return ctime(&ltime);
 }

example.i

/* example.i */
 %module example
 %{
 /* Put header files here or function declarations like below */
 extern double My_variable;
 extern int fact(int n);
 extern int my_mod(int x, int y);
 extern char *get_time();
 %}
 
 extern double My_variable;
 extern int fact(int n);
 extern int my_mod(int x, int y);
 extern char *get_time();

构建

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

当然可以自己修改 Python 的路径

使用的时候就直接在 Python 中 import example

pybind11

如果是使用 C++ 还可以用 pybind11 https://github.com/pybind/pybind11 比 swig 更轻,与之类似的还有 Boost.Python caffe 的 Python 就可以用 Boost.Python 编译。如果你主要使用的语言是 C++ 但是要给使用 Python 的用户提供相对友好的接口就可以使用这种方式。

example.c

#include <pybind11/pybind11.h>

int add(int i, int j) {
    return i + j;
}

PYBIND11_MODULE(example, m) {
    m.doc() = "pybind11 example plugin"; // optional module docstring

    m.def("add", &add, "A function which adds two numbers");
}

编译

c++ -O3 -Wall -shared -std=c++11 -fPIC `python3 -m pybind11 --includes` example.cpp -o example`python3-config --extension-suffix`

然后就可以在 Python 中直接导入了,前提是的安装 pybind11 。

总结

Python 语言本身是一种表达方式,通过 Python 解释器翻译成其他语言,由于 CPython 是 C 语言实现所有比较方便的可以在 C/C++ 和 Python 之间进行调用,问题就是这个翻译工作谁来做,怎么做,以及翻译方式是否方便和维护。


以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

The Probabilistic Method Second Edition

The Probabilistic Method Second Edition

Noga Alon、Joel H. Spencer / Wiley-Blackwell / 2000 / $121.95

The leading reference on probabilistic methods in combinatorics-now expanded and updated When it was first published in 1991, The Probabilistic Method became instantly the standard reference on one......一起来看看 《The Probabilistic Method Second Edition》 这本书的介绍吧!

XML、JSON 在线转换
XML、JSON 在线转换

在线XML、JSON转换工具

UNIX 时间戳转换
UNIX 时间戳转换

UNIX 时间戳转换

HSV CMYK 转换工具
HSV CMYK 转换工具

HSV CMYK互换工具