通过C API从字符串创建并调用Python函数
有没有办法从一个字符串加载一个Python函数,然后用参数调用这个函数并获取返回值呢?
我正在使用Python的C API在我的C++应用程序中运行Python代码。我可以通过PyImport_Import
从文件中加载一个模块,然后用PyObject_GetAttrString
从中获取一个函数对象,再用PyObject_CallObject
调用这个函数。我想做的是从一个字符串中加载模块或函数,而不是从文件中。有没有类似于PyImport_Import
的东西,可以让我传入一个字符串而不是文件?我需要给我调用的函数传递参数,并且需要获取返回值,所以我不能只用PyRun_SimpleString
。
编辑:
在了解到PyRun_String
后,我找到了这个解决方案。我创建了一个新模块,获取它的字典对象,然后在调用PyRun_String
时传入这个字典对象,以在我的新模块中定义一个函数,接着获取这个新创建的函数的函数对象,并通过PyObject_CallObject
调用它,传入我的参数。这是我找到的解决我问题的方法:
main.cpp
int main()
{
PyObject *pName, *pModule, *pArgs, *pValue, *pFunc;
PyObject *pGlobal = PyDict_New();
PyObject *pLocal;
//Create a new module object
PyObject *pNewMod = PyModule_New("mymod");
Py_Initialize();
PyModule_AddStringConstant(pNewMod, "__file__", "");
//Get the dictionary object from my module so I can pass this to PyRun_String
pLocal = PyModule_GetDict(pNewMod);
//Define my function in the newly created module
pValue = PyRun_String("def blah(x):\n\tprint 5 * x\n\treturn 77\n", Py_file_input, pGlobal, pLocal);
Py_DECREF(pValue);
//Get a pointer to the function I just defined
pFunc = PyObject_GetAttrString(pNewMod, "blah");
//Build a tuple to hold my arguments (just the number 4 in this case)
pArgs = PyTuple_New(1);
pValue = PyInt_FromLong(4);
PyTuple_SetItem(pArgs, 0, pValue);
//Call my function, passing it the number four
pValue = PyObject_CallObject(pFunc, pArgs);
Py_DECREF(pArgs);
printf("Returned val: %ld\n", PyInt_AsLong(pValue));
Py_DECREF(pValue);
Py_XDECREF(pFunc);
Py_DECREF(pNewMod);
Py_Finalize();
return 0;
}
以下是我原帖的其余部分,留作纪念:
这是我最初的做法:
main.cpp
:
#include <Python.h>
int main()
{
PyObject *pName, *pModule, *pArgs, *pValue, *pFunc;
Py_Initialize();
PyRun_SimpleString("import sys");
PyRun_SimpleString("sys.path.append('')");
pName = PyString_FromString("atest");
pModule = PyImport_Import(pName);
Py_DECREF(pName);
if(pModule == NULL)
{
printf("PMod is null\n");
PyErr_Print();
return 1;
}
pFunc = PyObject_GetAttrString(pModule, "doStuff");
pArgs = PyTuple_New(1);
pValue = PyInt_FromLong(4);
PyTuple_SetItem(pArgs, 0, pValue);
pValue = PyObject_CallObject(pFunc, pArgs);
Py_DECREF(pArgs);
printf("Returned val: %ld\n", PyInt_AsLong(pValue));
Py_DECREF(pValue);
Py_XDECREF(pFunc);
Py_DECREF(pModule);
Py_Finalize();
return 0;
}
还有atest.py
:
def doStuff( x):
print "X is %d\n" % x
return 2 * x
3 个回答
这个问题虽然有点老,但如果还有人像我一样在研究这个,我就分享一下我的答案。
最初的答案和@kmp更新的答案在简单的例子中是有效的,但一旦代码中引入了像import
这样的语句,就不再适用了。
我在这里找到一个有效的例子:https://awasu.com/weblog/embedding-python/writing-a-c-wrapper-library-part3/
简单来说(使用Python 3.6的API):
- 先读取一个Python文件:
std::string line, text;
std::ifstream in("the_file.py");
while (std::getline(in, line)) {
text += line + "\n";
}
const char *data = text.c_str();
- 然后把它编译成字节码:
PyObject *pCodeObj = Py_CompileString(data, "", Py_file_input);
- 接着通过执行代码来创建一个模块:
pModule = PyImport_ExecCodeModule("the_module", pCodeObj );
之后你可以继续使用PyObject_GetAttrString
来获取函数,并像上面的例子那样调用它们。
问题中的答案非常好,但我在使用它的时候遇到了一些小问题,特别是在Python 3.5上。为了避免其他人也遇到我遇到的麻烦,下面是一个稍微修改过的版本,至少在这个版本的Python上运行得很好:
#include <Python.h>
int main(void)
{
PyObject *pArgs, *pValue, *pFunc, *pModule, *pGlobal, *pLocal;
Py_Initialize();
pGlobal = PyDict_New();
//Create a new module object
pModule = PyModule_New("mymod");
PyModule_AddStringConstant(pModule, "__file__", "");
//Get the dictionary object from my module so I can pass this to PyRun_String
pLocal = PyModule_GetDict(pModule);
//Define my function in the newly created module
pValue = PyRun_String("def blah(x):\n\ty = x * 5\n\treturn y\n",
Py_file_input, pGlobal, pLocal);
//pValue would be null if the Python syntax is wrong, for example
if (pValue == NULL) {
if (PyErr_Occurred()) {
PyErr_Print();
}
return 1;
}
//pValue is the result of the executing code,
//chuck it away because we've only declared a function
Py_DECREF(pValue);
//Get a pointer to the function I just defined
pFunc = PyObject_GetAttrString(pModule, "blah");
//Double check we have actually found it and it is callable
if (!pFunc || !PyCallable_Check(pFunc)) {
if (PyErr_Occurred()) {
PyErr_Print();
}
fprintf(stderr, "Cannot find function \"blah\"\n");
return 2;
}
//Build a tuple to hold my arguments (just the number 4 in this case)
pArgs = PyTuple_New(1);
pValue = PyLong_FromLong(4);
PyTuple_SetItem(pArgs, 0, pValue);
//Call my function, passing it the number four
pValue = PyObject_CallObject(pFunc, pArgs);
fprintf(stdout, "Returned value: %ld\n", PyLong_AsLong(pValue));
Py_DECREF(pValue);
Py_XDECREF(pFunc);
Py_Finalize();
return 0;
}
在Python的C语言接口中,PyRun_String
可能正是你需要的东西。你可以查看这个链接了解更多信息:http://docs.python.org/c-api/veryhigh.html