2010-09-24 28 views
9

¿Es posible cargar una función python desde una cadena y luego llamar a esa función con argumentos y obtener el valor devuelto?Crear y llamar a la función python desde la cadena a través de C API

Estoy usando la API Python C para ejecutar el código python desde dentro de mi aplicación C++. Puedo cargar un módulo desde un archivo usando PyImport_Import, obtener un objeto de función del que usa PyObject_GetAttrString, y llamar a la función con PyObject_CallObject. Lo que me gustaría hacer es cargar el módulo/función de una cadena en lugar de un archivo. ¿Hay algún equivalente a PyImport_Import que me permita pasarle una cadena en lugar de un archivo? Necesito pasar argumentos a la función que estoy llamando y necesito acceder al valor de retorno, así que no puedo usar PyRun_SimpleString.


Editar:

He encontrado esta solución después de ser activado a PyRun_String. Estoy creando un nuevo módulo, obteniendo su objeto de diccionario, pasando eso en una llamada al PyRun_String para definir una función en mi nuevo módulo, luego obtengo un objeto de función para esa función recién creada y llamándola por PyObject_CallObject, pasando my args. Esto es lo que he encontrado para resolver mi problema: 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; 
} 

Aquí está el resto de mi post original, dejó para la posteridad:

Esto es lo que estaba haciendo originalmente: 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; 
} 

Y atest.py:


def doStuff(x): 
    print "X is %d\n" % x 
    return 2 * x 
+0

No es necesario dejar su publicación para la posteridad. Stack Overflow ya tiene eso cubierto. Todas las ediciones de preguntas y respuestas se archivan automáticamente, lo que podemos ver una vez que nuestros puntos alcanzan un cierto nivel. Eso nos ayuda a deshacer las malas ediciones y rastrear los cambios para comprender mejor las preguntas. Entonces, en lugar de dejarlo allí, puede eliminar sus cambios y podemos mirar el registro de edición si es necesario. Por eso, ni siquiera es necesario poner "Editar:". –

Respuesta

5

PyRun_String en la API Python C es probablemente lo que estás buscando. Ver: http://docs.python.org/c-api/veryhigh.html

+1

No está claro cómo transmitiría argumentos a la función. Parece que puedo definir una función usando 'PyRun_String', pero no veo cómo llamarlo con parámetros que no son solo cadenas codificadas. Es decir, podría llamar a PyRun_String nuevamente con "doStuff (7)" como primer argumento, pero eso no funcionará si el argumento es un objeto o algo así. ¿Hay alguna manera de llamar a PyObject_GetAttrString para obtener el objeto de función para doStuff si se ha definido a través de PyRun_String? – alanc10n

+0

Aceptar porque me inició en el camino para encontrar una solución, aunque tuve que hacer una gran cantidad de lecturas para llegar allí. Gracias. – alanc10n

+1

Lo siento, no estuve en línea durante las últimas horas, pero parece que lo descubrió exactamente.'PyRun_String' ejecuta un bloque de código Python a partir de una cadena y devuelve lo que el código hubiera devuelto; supongo que es' Ninguno' en su caso. Después de eso, la función que acaba de evaluar se agrega al espacio de nombres local; debe recuperarla desde allí usando 'PyObject_GetAttrString' y luego llamarla. También es posible que desee comprobar el valor de retorno de 'PyRun_String'; si es' NULL', hubo una excepción al ejecutar el bloque de código. –

3

La respuesta contenida en la pregunta es excelente pero tuve algunos pequeños problemas al usarla con Python 3.5, así que para salvar a alguien más haciendo lo que hice, debajo hay una versión ligeramente editada que parece funcionar bien para esta versión de Python al menos:

#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; 
} 
Cuestiones relacionadas