通过 C API 从字符串创建和调用 python 函数
Posted
技术标签:
【中文标题】通过 C API 从字符串创建和调用 python 函数【英文标题】:Create and call python function from string via C API 【发布时间】:2011-04-16 22:26:41 【问题描述】:是否可以从字符串加载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
【问题讨论】:
没有必要为后代留下您的帖子。 Stack Overflow 已经涵盖了这一点。对问题和答案的所有编辑都会自动存档,一旦我们的分数达到一定水平,我们就可以看到。这有助于我们回滚错误的编辑,并跟踪更改以更好地理解问题。因此,您可以删除您的更改,而不是将其留在那里,如果需要,我们可以查看编辑日志。因此,甚至不需要输入“编辑:”。 对于其他偶然发现此问题的人:在第一个代码 sn-p 中,调用 Py_Initialize 为时已晚。只是你知道 检查是否使用pValue两次第一次引用一定会造成内存泄漏 【参考方案1】:Python C API 中的PyRun_String
可能是您正在寻找的。见:http://docs.python.org/c-api/veryhigh.html
【讨论】:
不清楚如何将参数传递给函数。看起来我可以使用PyRun_String
定义一个函数,但我不知道如何使用不仅仅是硬编码字符串的参数来调用它。也就是说,我可以使用“doStuff(7)”作为第一个参数再次调用 PyRun_String,但如果参数是对象或类似的东西,这将不起作用。如果 doStuff 是通过 PyRun_String 定义的,有什么方法可以调用 PyObject_GetAttrString 来获取 doStuff 的函数对象?
接受,因为它让我开始了寻找解决方案的道路,尽管我必须阅读大量的资料才能到达那里。谢谢。
抱歉,我最近几个小时没上网,但看起来你已经猜到了。 PyRun_String
从字符串运行 Python 代码块并返回代码将返回的任何内容——我认为在你的情况下它是 None
。之后,您刚刚评估的函数被添加到本地命名空间——您必须使用PyObject_GetAttrString
从那里检索它,然后调用它。您可能还想检查 PyRun_String
的返回值 - 如果是 NULL
,则执行代码块时出现异常。【参考方案2】:
问题中包含的答案非常好,但我在使用 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;
【讨论】:
优秀的答案!对我非常有用。我在我的程序中使用了这段代码的很多部分。【参考方案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
获取函数并按照上述示例调用它们。
【讨论】:
以上是关于通过 C API 从字符串创建和调用 python 函数的主要内容,如果未能解决你的问题,请参考以下文章
通过 ctypes 从 Python 调用的 C 函数返回不正确的值