Создать и вызвать функцию Python из строки через C API - PullRequest
10 голосов
/ 24 сентября 2010

Можно ли загрузить функцию python из строки, а затем вызвать эту функцию с аргументами и получить возвращаемое значение?

Я использую API Python C для запуска кода Python из моего приложения C ++.,Я могу загрузить модуль из файла, используя 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 ]

5 голосов
/ 24 сентября 2010

PyRun_String в Python C API, вероятно, то, что вы ищете.Смотри: http://docs.python.org/c-api/veryhigh.html

4 голосов
/ 10 октября 2016

Ответ, содержащийся в вопросе, превосходен, но у меня возникли небольшие проблемы при использовании его с 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;
}
0 голосов
/ 12 июня 2019

Этот вопрос довольно старый, однако, если кто-то все еще пытается выяснить это (как я), я предоставлю свой ответ.

Исходный ответ и обновленный ответ @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 и вызывая их, как в примерах выше.

...