通过C API从字符串创建和调用python函数

时间:2010-09-24 18:28:06

标签: python c embed

是否可以从字符串加载python函数然后使用参数调用该函数并获取返回值?

我正在使用python C API从我的C ++应用程序中运行python代码。我可以使用PyImport_Import从文件加载模块,使用PyObject_GetAttrString从中获取一个函数对象,并使用PyObject_CallObject调用该函数。我想做的是从字符串而不是文件加载模块/函数。是否有一些等同于PyImport_Import允许我传递一个字符串而不是文件?我需要将参数传递给我正在调用的函数,并且我需要访问返回值,所以我不能只使用PyRun_SimpleString


修改

我在开启PyRun_String后找到了此解决方案。我正在创建一个新模块,获取它的字典对象,在调用PyRun_String时传递它以在我的新模块中定义一个函数,然后为新创建的函数获取一个函数对象并通过{{ 1}},通过我的args。这是我发现解决我的问题: 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

3 个答案:

答案 0 :(得分:5)

Python C API中的

PyRun_String可能就是你要找的东西。请参阅:http://docs.python.org/c-api/veryhigh.html

答案 1 :(得分:4)

问题中包含的答案非常好,但我在使用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;
}

答案 2 :(得分:0)

这个问题已经很老了,但是,如果有人仍然想弄清楚这个问题(像我以前一样),我会提供答案。

@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来获取函数,然后像上面的示例一样调用它们。