Python von C (C API)

Haupt

Letztes Jahr musste ein altes, in C geschriebenes Projekt durch Funktionen in Python3 ergänzt werden . Trotz der Tatsache, dass es Artikel zu diesem Thema gibt, habe ich sowohl in diesem Jahr als auch jetzt gelitten, als ich Programme für den Artikel schrieb. Daher werde ich meine Beispiele geben, wie man mit Python3 von C unter Linux arbeitet (mit dem, was ich verwendet habe). Ich werde beschreiben, wie eine Klasse erstellt und ihre Methoden aufgerufen werden, um auf Variablen zuzugreifen. Funktionen aufrufen und Variablen aus dem Modul abrufen. Und auch die Probleme, auf die ich gestoßen bin und die ich nicht verstehen konnte.


Pakete


Wir verwenden die Standard- Python-API für C. Erforderliche Python-Pakete:


  • python3
  • python3-dev
  • python3-all
  • python3-all-dev
  • libpython3-all-dev

Arbeiten Sie im Dolmetscher


Am einfachsten ist das Laden und Arbeiten im Python-Interpreter.


Um zu arbeiten, müssen Sie die Header-Datei verbinden:


#include <Python.h> 

Laden des Dolmetschers:


 Py_Initialize(); 

Als nächstes folgt ein Arbeitsblock mit Python, zum Beispiel:


 PyRun_SimpleString("print('Hello!')"); 

Entladen Sie den Dolmetscher:


 Py_Finalize(); 

Vollständiges Beispiel:


 #include <Python.h> void main() { //   Python Py_Initialize(); //     PyRun_SimpleString("print('Hello!')"); //   Python Py_Finalize(); } 

So kompilieren und ausführen Sie:


 gcc simple.c $(python3-config --includes --ldflags) -o simple && ./simple Hello! 

Das wird aber nicht funktionieren:


 gcc $(python3-config --includes --ldflags) simple.c -o simple && ./simple /tmp/ccUkmq57.o: In function `main': simple.c:(.text+0x5): undefined reference to `Py_Initialize' simple.c:(.text+0x16): undefined reference to `PyRun_SimpleStringFlags' simple.c:(.text+0x1b): undefined reference to `Py_Finalize' collect2: error: ld returned 1 exit status 

Dies liegt daran, dass python3-config --includes --ldflags zu folgenden Dingen erweitert wird:


 -I/usr/include/python3.6m -I/usr/include/python3.6m -L/usr/lib/python3.6/config-3.6m-x86_64-linux-gnu -L/usr/lib -lpython3.6m -lpthread -ldl -lutil -lm -Xlinker -export-dynamic -Wl,-O1 -Wl,-Bsymbolic-functions 

Hier halte ich die Verbindungsreihenfolge des -Wl- Linkers für wichtig . Wer es genauer weiß, schreibt darüber in Kommentaren, ich werde die Antwort ergänzen.


Erklärung von MooNDeaR :


Alles ist ganz einfach - Zeichen werden in einem Durchgang gesucht und alle nicht verwendeten Zeichen werden weggeworfen. Wenn Sie am Ende simple.c einfügen, stellt sich heraus, dass der Linker die Verwendung des Symbols Py_Initialize () sieht, nachdem er die Python-Bibliotheken betrachtet hat, deren Symbole zu diesem Zeitpunkt alle verworfen werden (weil sie nicht verwendet wurden).

Ein Beispiel für den Aufruf einer Funktion aus einer Python-Datei:
simple.c


 #include <Python.h> void python() { //   Python Py_Initialize(); //     //   sys PyRun_SimpleString("import sys"); //    python PyRun_SimpleString("sys.path.append('./src/python')"); PyRun_SimpleString("import simple"); PyRun_SimpleString("print(simple.get_value(2))"); PyRun_SimpleString("print(simple.get_value(2.0))"); PyRun_SimpleString("print(simple.get_value(\"Hello!\"))"); //   Python Py_Finalize(); } void main() { puts("Test simple:"); python(); } 

simple.py


 #!/usr/bin/python3 #-*- coding: utf-8 -*- def get_value(x): return x 

Aber das sind einfache und uninteressante Dinge, wir bekommen nicht das Ergebnis der Funktion.


Arbeiten Sie mit Funktionen und Modulvariablen


Das ist etwas kniffliger.
Laden des Python-Interpreters und des func.py-Moduls:


 PyObject * python_init() { //   Python Py_Initialize(); do { //   sys sys = PyImport_ImportModule("sys"); sys_path = PyObject_GetAttrString(sys, "path"); //     Python folder_path = PyUnicode_FromString((const char*) "./src/python"); PyList_Append(sys_path, folder_path); //  func.py pName = PyUnicode_FromString("func"); if (!pName) { break; } //    pModule = PyImport_Import(pName); if (!pModule) { break; } //      pDict = PyModule_GetDict(pModule); if (!pDict) { break; } return pDict; } while (0); //   PyErr_Print(); } 

Freigeben von Python-Interpreter-Ressourcen:


 void python_clear() { //    Py_XDECREF(pDict); Py_XDECREF(pModule); Py_XDECREF(pName); Py_XDECREF(folder_path); Py_XDECREF(sys_path); Py_XDECREF(sys); //   Python Py_Finalize(); } 

Arbeiten Sie mit Variablen und Modulfunktionen.


 /** *          */ char * python_func_get_str(char *val) { char *ret = NULL; //   get_value  func.py pObjct = PyDict_GetItemString(pDict, (const char *) "get_value"); if (!pObjct) { return ret; } do { //  pObjct  . if (!PyCallable_Check(pObjct)) { break; } pVal = PyObject_CallFunction(pObjct, (char *) "(s)", val); if (pVal != NULL) { PyObject* pResultRepr = PyObject_Repr(pVal); //     ,     Python   . //   pResultRepr     . ret = strdup(PyBytes_AS_STRING(PyUnicode_AsEncodedString(pResultRepr, "utf-8", "ERROR"))); Py_XDECREF(pResultRepr); Py_XDECREF(pVal); } else { PyErr_Print(); } } while (0); return ret; } /** *       int */ int python_func_get_val(char *val) { int ret = 0; //     val pVal = PyDict_GetItemString(pDict, (const char *) val); if (!pVal) { return ret; } //    long if (PyLong_Check(pVal)) { ret = _PyLong_AsInt(pVal); } else { PyErr_Print(); } return ret; } 

Lassen Sie uns näher darauf eingehen.


 pVal = PyObject_CallFunction(pObjct, (char *) "(s)", val); 

"(s)" bedeutet, dass 1 char * -Parameter als Argument an get_value (x) übergeben wird . Wenn wir der Funktion mehrere Argumente übergeben müssen, sieht dies folgendermaßen aus:


 pVal = PyObject_CallFunction(pObjct, (char *) "(sss)", val1, val2, val3); 

Wenn Sie int übergeben müssen , wird der Buchstabe i verwendet. Alle möglichen Datentypen und ihre Bezeichnungen finden Sie in der Python- Dokumentation .


 pVal = PyObject_CallFunction(pObjct, (char *) "(i)", my_int); 

func.py:


 #!/usr/bin/python3 #-*- coding: utf-8 -*- a = 11 b = 22 c = 33 def get_value(x): return x def get_bool(self, x): if x: return True else: return False 

( Problem behoben )
Das Problem, auf das ich gestoßen bin und das ich noch nicht verstehen konnte:


 int main() { puts("Test func:"); if (!python_init()) { puts("python_init error"); return -1; } puts("Strings:"); printf("\tString: %s\n", python_func_get_str("Hello from Python!")); puts("Attrs:"); printf("\ta: %d\n", python_func_get_val("a")); printf("\tb: %d\n", python_func_get_val("b")); printf("\tc: %d\n", python_func_get_val("c")); python_clear(); return 0; } 

Wenn ich b oder c von func.py erhalten möchte , dann auf:


 Py_Finalize(); 

Ich erhalte einen Segmentierungsfehler . Es gibt kein solches Problem, nur eine zu bekommen .
Beim Empfang von Klassenvariablen gibt es ebenfalls keine Probleme.


Erklärung von pwl :


PyObject PyDict_GetItemString (PyObject p, const char * key)
Rückgabewert: Ausgeliehene Referenz. Für eine geliehene Referenz muss nichts getan werden.

Das Problem war, dass ich Py_XDECREF () für PyDict_GetItemString () aufrief . Dies ist für diese Funktion nicht erforderlich, was zu einem Segmentierungsfehler führt .


Klassenarbeit


Es ist noch etwas komplizierter.
Laden des Python-Interpreters und des class.py-Moduls.


 PyObject * python_init() { //   Python Py_Initialize(); do { //   sys sys = PyImport_ImportModule("sys"); sys_path = PyObject_GetAttrString(sys, "path"); //     Python folder_path = PyUnicode_FromString((const char*) "./src/python"); PyList_Append(sys_path, folder_path); //  Unicode   UTF-8  pName = PyUnicode_FromString("class"); if (!pName) { break; } //   class pModule = PyImport_Import(pName); if (!pModule) { break; } //      pDict = PyModule_GetDict(pModule); if (!pDict) { break; } //   Class  class.py pClass = PyDict_GetItemString(pDict, (const char *) "Class"); if (!pClass) { break; } //  pClass  . if (!PyCallable_Check(pClass)) { break; } //   Class pInstance = PyObject_CallObject(pClass, NULL); return pInstance; } while (0); //   PyErr_Print(); } 

Übergeben einer Zeichenfolge als Argument und Zurückholen der Zeichenfolge


 char * python_class_get_str(char *val) { char *ret = NULL; pVal = PyObject_CallMethod(pInstance, (char *) "get_value", (char *) "(s)", val); if (pVal != NULL) { PyObject* pResultRepr = PyObject_Repr(pVal); //     ,     Python   . ret = strdup(PyBytes_AS_STRING(PyUnicode_AsEncodedString(pResultRepr, "utf-8", "ERROR"))); Py_XDECREF(pResultRepr); Py_XDECREF(pVal); } else { PyErr_Print(); } return ret; } 

Hier gab es keine Probleme, alles funktioniert fehlerfrei. In der Quelle finden Sie Beispiele für die Arbeit mit int , double , bool .


Beim Schreiben von Material habe ich mein Wissen aufgefrischt.
Ich hoffe es wird nützlich sein.


Referenzen


Source: https://habr.com/ru/post/de466181/


All Articles