[英]How do you call Python code from C code?
我想用一些新功能擴展一個大型 C 項目,但我真的想用 Python 編寫它。 基本上,我想從 C 代碼調用 Python 代碼。 但是,像 SWIG 這樣的 Python->C 包裝器允許 OPPOSITE,即編寫 C 模塊並從 Python 調用 C。
我正在考慮一種涉及 IPC 或 RPC 的方法(我不介意有多個進程); 也就是說,讓我的純 Python 組件在單獨的進程中運行(在同一台機器上),並讓我的 C 項目通過從套接字(或 unix 管道)寫入/讀取來與其通信。 我的 python 組件可以讀/寫套接字進行通信。 這是一個合理的方法嗎? 有什么更好的嗎? 喜歡一些特殊的RPC機制?
感謝到目前為止的回答 -但是,我想專注於基於 IPC 的方法,因為我希望將我的 Python 程序作為我的 C 程序放在一個單獨的進程中。 我不想嵌入 Python 解釋器。 謝謝!
我推薦這里詳述的方法。 它首先解釋如何執行 Python 代碼字符串,然后詳細說明如何設置 Python 環境以與 C 程序交互、從 C 代碼調用 Python 函數、從 C 代碼操作 Python 對象等。
編輯:如果你真的想走 IPC 的路線,那么你會想要使用struct 模塊或更好的protlib 。 Python 和 C 進程之間的大多數通信都圍繞着來回傳遞結構,通過套接字或通過共享內存。
我建議創建一個帶有字段和代碼的Command
結構來表示命令及其參數。 在不了解您想要完成的任務的情況下,我無法給出更具體的建議,但總的來說,我推薦protlib庫,因為我用它來在 C 和 Python 程序之間進行通信(免責聲明:我是 protlib 的作者) .
您是否考慮過將您的 Python 應用程序包裝在一個 shell 腳本中並從您的 C 應用程序中調用它?
不是最優雅的解決方案,但它非常簡單。
請參閱手冊中的相關章節: http : //docs.python.org/extending/
本質上,您必須將 python 解釋器嵌入到您的程序中。
我沒有使用 IPC 方法進行 Python<->C 通信,但它應該工作得很好。 我會讓 C 程序做一個標准的 fork-exec 並在子進程中使用重定向的stdin
和stdout
進行通信。 良好的基於文本的通信將使開發和測試 Python 程序變得非常容易。
這看起來很不錯http://thrift.apache.org/ ,甚至還有一本關於它的書。
細節:
Apache Thrift 軟件框架,用於可擴展的跨語言服務開發,將軟件堆棧與代碼生成引擎相結合,構建在 C++、Java、Python、PHP、Ruby、Erlang、Perl、Haskell、C#、 Cocoa、JavaScript、Node.js、Smalltalk、OCaml 和 Delphi 等語言。
我使用了在另一個應用程序中嵌入 Python的“標准”方法。 但這很復雜/乏味。 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'
$
顯然Python需要能夠編譯為win32 dll,它會解決問題
以這種方式將 c# 代碼轉換為 win32 dll 將使其可用於任何開發工具
將它與 Python 3 綁定的幾個技巧
ffi.cdef(open('interface.h').read())
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") )
wchar_t *name = Py_DecodeLocale(argv[0], NULL);
Py_SetProgramName(name);
gcc cc.c -o cc -I/usr/include/python3.6m -I/usr/include/x86_64-linux-gnu/python3.6m -lpython3.6m
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
}
希望它會有所幫助並為您節省一些時間
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.