webentwicklung-frage-antwort-db.com.de

Aufrufen einer Python-Methode aus C/C++ und Extrahieren des Rückgabewerts

Ich möchte eine benutzerdefinierte Funktion aufrufen, die in einem Python-Modul von C definiert ist. Ich habe dazu einen vorläufigen Code, druckt aber nur die Ausgabe in stdout.

mytest.py

import math

def myabs(x):
    return math.fabs(x)

test.cpp

#include <Python.h>

int main() {
    Py_Initialize();
    PyRun_SimpleString("import sys; sys.path.append('.')");
    PyRun_SimpleString("import mytest;");
    PyRun_SimpleString("print mytest.myabs(2.0)");
    Py_Finalize();

    return 0;
}

Wie kann ich den Rückgabewert in ein C-Double extrahieren und in C verwenden?

41
dzhelil

Wie bereits erwähnt, scheint die Verwendung von PyRun_SimpleString eine schlechte Idee zu sein.

Sie sollten auf jeden Fall die von der C-API bereitgestellten Methoden verwenden ( http://docs.python.org/c-api/ ).

Das Lesen der Einführung ist das erste, was zu verstehen ist, wie es funktioniert.

Zuerst müssen Sie etwas über PyObject erfahren, das das Basisobjekt für die C-API ist. Es kann jede Art von Python-Basistypen darstellen (string, float, int, ...).

Es gibt viele Funktionen, um beispielsweise Python-Zeichenfolgen in char * oder PyFloat in double umzuwandeln.

Importieren Sie zunächst Ihr Modul:

PyObject* myModuleString = PyString_FromString((char*)"mytest");
PyObject* myModule = PyImport_Import(myModuleString);

Dann erhalten Sie einen Hinweis auf Ihre Funktion:

PyObject* myFunction = PyObject_GetAttrString(myModule,(char*)"myabs");
PyObject* args = PyTuple_Pack(1,PyFloat_FromDouble(2.0));

Dann erhalten Sie Ihr Ergebnis:

PyObject* myResult = PyObject_CallObject(myFunction, args)

Und zurück zu einem Double:

double result = PyFloat_AsDouble(myResult);

Sie sollten natürlich die Fehler überprüfen (siehe Link von Mark Tolonen).

Wenn Sie Fragen haben, zögern Sie nicht. Viel Glück.

57
ThR37

Hier ist ein Beispielcode, den ich geschrieben habe (mit Hilfe verschiedener Online-Quellen), um einen String an einen Python-Code zu senden und dann einen Wert zurückzugeben.

Hier ist der C-Code call_function.c:

#include <Python.h>
#include <stdlib.h>
int main()
{
   // Set PYTHONPATH TO working directory
   setenv("PYTHONPATH",".",1);

   PyObject *pName, *pModule, *pDict, *pFunc, *pValue, *presult;


   // Initialize the Python Interpreter
   Py_Initialize();


   // Build the name object
   pName = PyString_FromString((char*)"arbName");

   // Load the module object
   pModule = PyImport_Import(pName);


   // pDict is a borrowed reference 
   pDict = PyModule_GetDict(pModule);


   // pFunc is also a borrowed reference 
   pFunc = PyDict_GetItemString(pDict, (char*)"someFunction");

   if (PyCallable_Check(pFunc))
   {
       pValue=Py_BuildValue("(z)",(char*)"something");
       PyErr_Print();
       printf("Let's give this a shot!\n");
       presult=PyObject_CallObject(pFunc,pValue);
       PyErr_Print();
   } else 
   {
       PyErr_Print();
   }
   printf("Result is %d\n",PyInt_AsLong(presult));
   Py_DECREF(pValue);

   // Clean up
   Py_DECREF(pModule);
   Py_DECREF(pName);

   // Finish the Python Interpreter
   Py_Finalize();


    return 0;
}

Hier ist der Python-Code in der Datei arbName.py:

 def someFunction(text):
    print 'You passed this Python program '+text+' from C! Congratulations!'
    return 12345

Ich benutze den Befehl gcc call_function.c -I/usr/include/python2.6 -lpython2.6 ; ./a.out, um diesen Prozess auszuführen. Ich bin im Red Hat. Ich empfehle, PyErr_Print () zu verwenden. zur Fehlerüberprüfung.

16
kilojoules

Ein vollständiges Beispiel zum Aufrufen einer Python-Funktion und zum Abrufen des Ergebnisses finden Sie unter http://docs.python.org/release/2.6.5/extending/embedding.html#pure-embedding :

#include <Python.h>

int
main(int argc, char *argv[])
{
    PyObject *pName, *pModule, *pDict, *pFunc;
    PyObject *pArgs, *pValue;
    int i;

    if (argc < 3) {
        fprintf(stderr,"Usage: call pythonfile funcname [args]\n");
        return 1;
    }

    Py_Initialize();
    pName = PyString_FromString(argv[1]);
    /* Error checking of pName left out */

    pModule = PyImport_Import(pName);
    Py_DECREF(pName);

    if (pModule != NULL) {
        pFunc = PyObject_GetAttrString(pModule, argv[2]);
        /* pFunc is a new reference */

        if (pFunc && PyCallable_Check(pFunc)) {
            pArgs = PyTuple_New(argc - 3);
            for (i = 0; i < argc - 3; ++i) {
                pValue = PyInt_FromLong(atoi(argv[i + 3]));
                if (!pValue) {
                    Py_DECREF(pArgs);
                    Py_DECREF(pModule);
                    fprintf(stderr, "Cannot convert argument\n");
                    return 1;
                }
                /* pValue reference stolen here: */
                PyTuple_SetItem(pArgs, i, pValue);
            }
            pValue = PyObject_CallObject(pFunc, pArgs);
            Py_DECREF(pArgs);
            if (pValue != NULL) {
                printf("Result of call: %ld\n", PyInt_AsLong(pValue));
                Py_DECREF(pValue);
            }
            else {
                Py_DECREF(pFunc);
                Py_DECREF(pModule);
                PyErr_Print();
                fprintf(stderr,"Call failed\n");
                return 1;
            }
        }
        else {
            if (PyErr_Occurred())
                PyErr_Print();
            fprintf(stderr, "Cannot find function \"%s\"\n", argv[2]);
        }
        Py_XDECREF(pFunc);
        Py_DECREF(pModule);
    }
    else {
        PyErr_Print();
        fprintf(stderr, "Failed to load \"%s\"\n", argv[1]);
        return 1;
    }
    Py_Finalize();
    return 0;
}
7
Mark Tolonen

Um die zusätzliche .py-Datei wie in den anderen Antworten zu verhindern, können Sie einfach das __main__-Modul abrufen, das beim ersten Aufruf von PyRun_SimpleString erstellt wird:

PyObject *moduleMainString = PyString_FromString("__main__");
PyObject *moduleMain = PyImport_Import(moduleMainString);

PyRun_SimpleString(
    "def mul(a, b):                                 \n"\
    "   return a * b                                \n"\
);

PyObject *func = PyObject_GetAttrString(moduleMain, "mul");
PyObject *args = PyTuple_Pack(2, PyFloat_FromDouble(3.0), PyFloat_FromDouble(4.0));

PyObject *result = PyObject_CallObject(func, args);

printf("mul(3,4): %.2f\n", PyFloat_AsDouble(result)); // 12
3
lama12345

Sie müssen die Python-Methode irgendwie extrahieren und mit PyObject_CallObject() ausführen. Um dies zu tun, können Sie Python eine Methode zum Festlegen der Funktion bereitstellen, wie dies im Beispiel Python-Lernprogramm für Erweitern und Einbetten der Fall ist.

Wenn Sie den Rückgabewert einer Variablen zuweisen, können Sie etwas wie PyEval_GetGlobals () und PyDict_GetItemString () verwenden, um das PyObject abzurufen. Von dort aus kann PyNumber_Float den gewünschten Wert erhalten.

Ich schlage vor, die gesamte API zu durchsuchen - bestimmte Dinge werden offensichtlich, wenn Sie die verschiedenen Methoden sehen, die Ihnen zur Verfügung stehen, und es kann eine bessere Methode geben, als die, die ich beschrieben habe.

1
Kylotan

Ich habe es mit BOOST getan, um Python in C++ einzubetten.

#include <boost/python.hpp>

void main()
{
using namespace boost::python;
 Py_Initialize();
 PyObject* filename = PyString_FromString((char*)"memory_leak_test");
     PyObject* imp = PyImport_Import(filename);
     PyObject* func = PyObject_GetAttrString(imp,(char*)"begin");
     PyObject* args = PyTuple_Pack(1,PyString_FromString("CacheSetup"));
     PyObject* retured_value = PyObject_CallObject(func, args); // if you have arg
     double retured_value = PyFloat_AsDouble(myResult);
 std::cout << result << std::endl;
 Py_Finalize();
}
0
user3758062