49

我想用一些新功能扩展一个大型 C 项目,但我真的想用 Python 编写它。基本上,我想从 C 代码中调用 Python 代码。但是,像 SWIG 这样的 Python->C 包装器允许 OPPOSITE,即编写 C 模块并从 Python 调用 C。

我正在考虑一种涉及 IPC 或 RPC 的方法(我不介意有多个进程);也就是说,让我的纯 Python 组件在单独的进程中(在同一台机器上)运行,并让我的 C 项目通过从套接字(或 unix 管道)写入/读取来与其通信。我的 python 组件可以读/写套接字进行通信。这是一个合理的方法吗?有更好的吗?像一些特殊的 RPC 机制?

感谢到目前为止的回答 -但是,我想专注于基于 IPC 的方法,因为我希望将我的 Python 程序作为我的 C 程序放在一个单独的进程中。我不想嵌入 Python 解释器。谢谢!

4

9 回答 9

16

我推荐这里详述的方法。它首先解释如何执行 Python 代码字符串,然后详细说明如何设置 Python 环境以与 C 程序交互、从 C 代码调用 Python 函数、从 C 代码操作 Python 对象等。

编辑:如果你真的想走 IPC 的路线,那么你会想要使用struct 模块或者更好的是protlib。Python 和 C 进程之间的大多数通信都围绕着来回传递结构,无论是通过套接字还是通过共享内存

我建议创建一个Command包含字段和代码的结构来表示命令及其参数。如果不了解您想要完成的工作,我无法提供更具体的建议,但总的来说,我推荐protlib库,因为它是我用来在 C 和 Python 程序之间进行通信的工具(免责声明:我是 protlib 的作者) .

于 2009-06-28T23:42:27.610 回答
7

您是否考虑过将您的 python 应用程序包装在一个 shell 脚本中并从您的 C 应用程序中调用它?

不是最优雅的解决方案,但它非常简单。

于 2009-06-29T00:00:42.160 回答
5

请参阅手册中的相关章节:http: //docs.python.org/extending/

本质上,您必须将 python 解释器嵌入到您的程序中。

于 2009-06-28T23:48:34.290 回答
1

我没有使用 IPC 方法进行 Python<->C 通信,但它应该工作得很好。我会让 C 程序执行标准的 fork-exec 并使用重定向stdinstdout在子进程中进行通信。良好的基于​​文本的通信将使 Python 程序的开发和测试变得非常容易。

于 2009-06-29T00:15:01.413 回答
1

如果我决定使用 IPC,我可能会大手大脚地使用XML-RPC——跨平台,如果你愿意,以后可以轻松地将 Python 服务器项目放在不同的节点上,有许多出色的实现(请参阅此处了解许多,包括 C 和 Python 的,这里是简单的 XML-RPC 服务器,它是 Python 标准库的一部分——不像其他方法那样具有高度可扩展性,但对于您的用例来说可能很好且方便)。

对于所有情况,它可能不是一个完美的 IPC 方法(或者甚至是完美的 RPC 方法,无论如何!),但在我看来,它的便利性、灵活性、健壮性和广泛的实现范围超过了许多小缺陷。

于 2009-06-29T02:44:04.450 回答
1

这似乎相当不错http://thrift.apache.org/,甚至有一本书关于它。

细节:

Apache Thrift 软件框架,用于可扩展的跨语言服务开发,将软件堆栈与代码生成引擎相结合,以构建在 C++、Java、Python、PHP、Ruby、Erlang、Perl、Haskell、C#、 Cocoa、JavaScript、Node.js、Smalltalk、OCaml 和 Delphi 等语言。

于 2015-09-12T06:40:06.357 回答
1

我使用了Embedding Python in Another Application的“标准”方法。但这很复杂/乏味。Python 中的每个新函数实现起来都很痛苦。

我看到了一个从 C 调用 PyPy的例子。它使用 CFFI 来简化界面,但它需要 PyPy,而不是 Python。首先阅读并理解这个例子,至少在高层次上。

我修改了 C/PyPy 示例以使用 Python。以下是如何使用 CFFI 从 C 调用 Python。

我的示例更复杂,因为我在 Python 中实现了三个函数而不是一个。我想介绍来回传递数据的其他方面。

复杂的部分现在被隔离到将地址传递api给 Python。这只需要执行一次。之后,很容易在 Python 中添加新函数。

接口.h

// These are the three functions that I implemented in Python.
// Any additional function would be added here.
struct API {
    double (*add_numbers)(double x, double y);
    char* (*dump_buffer)(char *buffer, int buffer_size);
    int (*release_object)(char *obj);
};

test_cffi.c

//
// Calling Python from C.
// Based on Calling PyPy from C:
// http://doc.pypy.org/en/latest/embedding.html#more-complete-example
//

#include <stdio.h>
#include <assert.h>

#include "Python.h"

#include "interface.h"

struct API api;   /* global var */

int main(int argc, char *argv[])
{
    int rc;

    // Start Python interpreter and initialize "api" in interface.py using 
    // old style "Embedding Python in Another Application":
    // https://docs.python.org/2/extending/embedding.html#embedding-python-in-another-application
    PyObject *pName, *pModule, *py_results;
    PyObject *fill_api;
#define PYVERIFY(exp) if ((exp) == 0) { fprintf(stderr, "%s[%d]: ", __FILE__, __LINE__); PyErr_Print(); exit(1); }

    Py_SetProgramName(argv[0]);  /* optional but recommended */
    Py_Initialize();
    PyRun_SimpleString(
            "import sys;"
            "sys.path.insert(0, '.')" );

    PYVERIFY( pName = PyString_FromString("interface") )
    PYVERIFY( pModule = PyImport_Import(pName) )
    Py_DECREF(pName);
    PYVERIFY( fill_api = PyObject_GetAttrString(pModule, "fill_api") )

    // "k" = [unsigned long],
    // see https://docs.python.org/2/c-api/arg.html#c.Py_BuildValue
    PYVERIFY( py_results = PyObject_CallFunction(fill_api, "k", &api) )
    assert(py_results == Py_None);

    // Call Python function from C using cffi.
    printf("sum: %f\n", api.add_numbers(12.3, 45.6));

    // More complex example.
    char buffer[20];
    char * result = api.dump_buffer(buffer, sizeof buffer);
    assert(result != 0);
    printf("buffer: %s\n", result);

    // Let Python perform garbage collection on result now.
    rc = api.release_object(result);
    assert(rc == 0);

    // Close Python interpreter.
    Py_Finalize();

    return 0;
}

接口.py

import cffi
import sys
import traceback

ffi = cffi.FFI()
ffi.cdef(file('interface.h').read())

# Hold references to objects to prevent garbage collection.
noGCDict = {}

# Add two numbers.
# This function was copied from the PyPy example.
@ffi.callback("double (double, double)")
def add_numbers(x, y):
    return x + y

# Convert input buffer to repr(buffer).
@ffi.callback("char *(char*, int)")
def dump_buffer(buffer, buffer_len):
    try:
        # First attempt to access data in buffer.
        # Using the ffi/lib objects:
        # http://cffi.readthedocs.org/en/latest/using.html#using-the-ffi-lib-objects
        # One char at time, Looks inefficient.
        #data = ''.join([buffer[i] for i in xrange(buffer_len)])

        # Second attempt.
        # FFI Interface:
        # http://cffi.readthedocs.org/en/latest/using.html#ffi-interface
        # Works but doc says "str() gives inconsistent results".
        #data = str( ffi.buffer(buffer, buffer_len) )

        # Convert C buffer to Python str.
        # Doc says [:] is recommended instead of str().
        data = ffi.buffer(buffer, buffer_len)[:]

        # The goal is to return repr(data)
        # but it has to be converted to a C buffer.
        result = ffi.new('char []', repr(data))

        # Save reference to data so it's not freed until released by C program.
        noGCDict[ffi.addressof(result)] = result

        return result
    except:
        print >>sys.stderr, traceback.format_exc()
        return ffi.NULL

# Release object so that Python can reclaim the memory.
@ffi.callback("int (char*)")
def release_object(ptr):
    try:
        del noGCDict[ptr]
        return 0
    except:
        print >>sys.stderr, traceback.format_exc()
        return 1

def fill_api(ptr):
    global api
    api = ffi.cast("struct API*", ptr)

    api.add_numbers = add_numbers
    api.dump_buffer = dump_buffer
    api.release_object = release_object

编译:

gcc -o test_cffi test_cffi.c -I/home/jmudd/pgsql-native/Python-2.7.10.install/include/python2.7 -L/home/jmudd/pgsql-native/Python-2.7.10.install/lib -lpython2.7

执行:

$ test_cffi
sum: 57.900000
buffer: 'T\x9e\x04\x08\xa8\x93\xff\xbf]\x86\x04\x08\x00\x00\x00\x00\x00\x00\x00\x00'
$ 
于 2015-11-23T15:51:23.417 回答
0

显然Python需要能够编译成win32 dll,这样就可以解决问题了

以这样一种方式,将 c# 代码转换为 win32 dll 将使其可用于任何开发工具

于 2009-10-25T17:16:09.323 回答
0

将它与 Python 3 绑定的一些技巧

  1. 不支持 file(),使用 open()

ffi.cdef(open('interface.h').read())

  1. PyObject* PyStr_FromString(const char *u)
    Create a PyStr from a UTF-8 encoded null-terminated character buffer.
    Python 2: PyString_FromString
    Python 3: PyUnicode_FromString

更改为:PYVERIFY(pName = PyUnicode_FromString("interface"))

  1. 节目名称
    wchar_t *name = Py_DecodeLocale(argv[0], NULL);
    Py_SetProgramName(name); 
  1. 用于编译
   gcc cc.c -o cc -I/usr/include/python3.6m -I/usr/include/x86_64-linux-gnu/python3.6m -lpython3.6m
  1. 我屠杀了转储def ..也许它会给出一些想法
def get_prediction(buffer, buffer_len):
    try:
        data = ffi.buffer(buffer, buffer_len)[:]
        result = ffi.new('char []', data)
      
        print('\n I am doing something here here........',data )

        resultA = ffi.new('char []', b"Failed")  ### New message 

        ##noGCDict[ffi.addressof(resultA)] = resultA 
        return resultA
    except:
        print >>sys.stderr, traceback.format_exc()
        return ffi.NULL
} 

希望它会对您有所帮助并节省一些时间

于 2020-07-26T01:21:53.480 回答