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
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() {
Freigeben von Python-Interpreter-Ressourcen:
void python_clear() {
Arbeiten Sie mit Variablen und Modulfunktionen.
char * python_func_get_str(char *val) { char *ret = NULL;
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:
( 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() {
Ü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);
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