Extending/Embedding FAQ

Kann ich meine eigenen Funktionen in C erstellen?

Ja, Sie können eingebaute Module mit Funktionen, Variablen, Ausnahmen und sogar neuen Typen in C erstellen. Dies wird im Dokument Erweitern und Einbetten des Python-Interpreters erklärt.

Die meisten mittleren oder fortgeschrittenen Python-Bücher behandeln dieses Thema ebenfalls.

Kann ich meine eigenen Funktionen in C++ erstellen?

Ja, unter Verwendung der C-Kompatibilitätsfunktionen von C++. Umschließen Sie die Python-Include-Dateien mit extern "C" { ... } und setzen Sie extern "C" vor jede Funktion, die vom Python-Interpreter aufgerufen werden soll. Globale oder statische C++-Objekte mit Konstruktoren sind wahrscheinlich keine gute Idee.

C schreiben ist schwierig; gibt es Alternativen?

Es gibt eine Reihe von Alternativen zum Schreiben eigener C-Erweiterungen, je nachdem, was Sie tun möchten. Empfohlene Drittanbieter-Tools bieten sowohl einfachere als auch ausgefeiltere Ansätze zur Erstellung von C- und C++-Erweiterungen für Python.

Wie kann ich beliebige Python-Anweisungen aus C ausführen?

Die Funktion auf höchster Ebene dafür ist PyRun_SimpleString(), die ein einzelnes String-Argument entgegennimmt, das im Kontext des Moduls __main__ ausgeführt wird, und 0 für Erfolg und -1 zurückgibt, wenn eine Ausnahme aufgetreten ist (einschließlich SyntaxError). Wenn Sie mehr Kontrolle wünschen, verwenden Sie PyRun_String(); siehe den Quellcode von PyRun_SimpleString() in Python/pythonrun.c.

Wie kann ich einen beliebigen Python-Ausdruck aus C auswerten?

Rufen Sie die Funktion PyRun_String() aus der vorherigen Frage mit dem Startsymbol Py_eval_input auf; diese parst einen Ausdruck, wertet ihn aus und gibt dessen Wert zurück.

Wie extrahiere ich C-Werte aus einem Python-Objekt?

Das hängt vom Typ des Objekts ab. Wenn es sich um ein Tupel handelt, gibt PyTuple_Size() seine Länge zurück und PyTuple_GetItem() gibt das Element an einem bestimmten Index zurück. Listen haben ähnliche Funktionen, PyList_Size() und PyList_GetItem().

Für Bytes gibt PyBytes_Size() seine Länge zurück und PyBytes_AsStringAndSize() stellt einen Zeiger auf seinen Wert und seine Länge bereit. Beachten Sie, dass Python-Byte-Objekte Null-Bytes enthalten können, daher sollte das C-eigene strlen() nicht verwendet werden.

Um den Typ eines Objekts zu prüfen, stellen Sie zunächst sicher, dass es nicht NULL ist, und verwenden Sie dann PyBytes_Check(), PyTuple_Check(), PyList_Check() usw.

Es gibt auch eine High-Level-API für Python-Objekte, die von der sogenannten „abstrakten“ Schnittstelle bereitgestellt wird – lesen Sie Include/abstract.h für weitere Details. Sie ermöglicht die Interaktion mit jeder Art von Python-Sequenz über Aufrufe wie PySequence_Length(), PySequence_GetItem() usw. sowie viele andere nützliche Protokolle wie Zahlen (PyNumber_Index() et al.) und Mappings in den PyMapping-APIs.

Wie verwende ich Py_BuildValue(), um ein Tupel beliebiger Länge zu erstellen?

Das geht nicht. Verwenden Sie stattdessen PyTuple_Pack().

Wie rufe ich die Methode eines Objekts aus C auf?

Die Funktion PyObject_CallMethod() kann verwendet werden, um eine beliebige Methode eines Objekts aufzurufen. Die Parameter sind das Objekt, der Name der aufzurufenden Methode, ein Formatstring wie der für Py_BuildValue() verwendete und die Argumentwerte.

PyObject *
PyObject_CallMethod(PyObject *object, const char *method_name,
                    const char *arg_format, ...);

Dies funktioniert für jedes Objekt, das Methoden hat – egal ob eingebaut oder benutzerdefiniert. Sie sind dafür verantwortlich, den Rückgabewert schließlich mit Py_DECREF() zu dekrementieren.

Um z.B. die „seek“-Methode eines Dateiobjekts mit den Argumenten 10, 0 aufzurufen (vorausgesetzt, der Zeiger auf das Dateiobjekt ist „f“)

res = PyObject_CallMethod(f, "seek", "(ii)", 10, 0);
if (res == NULL) {
        ... an exception occurred ...
}
else {
        Py_DECREF(res);
}

Beachten Sie, dass PyObject_CallObject() *immer* ein Tupel für die Argumentliste erwartet. Um eine Funktion ohne Argumente aufzurufen, übergeben Sie „()“ für das Format, und um eine Funktion mit einem Argument aufzurufen, umschließen Sie das Argument in Klammern, z.B. „(i)“.

Wie fange ich die Ausgabe von PyErr_Print() (oder allem, was auf stdout/stderr druckt) ab?

Definieren Sie im Python-Code ein Objekt, das die write()-Methode unterstützt. Weisen Sie dieses Objekt sys.stdout und sys.stderr zu. Rufen Sie print_error auf oder lassen Sie einfach den Standard-Traceback-Mechanismus funktionieren. Dann geht die Ausgabe dorthin, wo Ihre write()-Methode sie hinleitet.

Der einfachste Weg, dies zu tun, ist die Verwendung der io.StringIO-Klasse.

>>> import io, sys
>>> sys.stdout = io.StringIO()
>>> print('foo')
>>> print('hello world!')
>>> sys.stderr.write(sys.stdout.getvalue())
foo
hello world!

Ein benutzerdefiniertes Objekt, das dasselbe tut, würde so aussehen:

>>> import io, sys
>>> class StdoutCatcher(io.TextIOBase):
...     def __init__(self):
...         self.data = []
...     def write(self, stuff):
...         self.data.append(stuff)
...
>>> import sys
>>> sys.stdout = StdoutCatcher()
>>> print('foo')
>>> print('hello world!')
>>> sys.stderr.write(''.join(sys.stdout.data))
foo
hello world!

Wie greife ich auf ein in Python geschriebenes Modul von C aus zu?

Sie können einen Zeiger auf das Modulobjekt wie folgt erhalten:

module = PyImport_ImportModule("<modulename>");

Wenn das Modul noch nicht importiert wurde (d.h. es ist noch nicht in sys.modules vorhanden), initialisiert dies das Modul; andernfalls gibt es einfach den Wert von sys.modules["<modulename>"] zurück. Beachten Sie, dass es das Modul nicht in irgendeinen Namespace einträgt – es stellt lediglich sicher, dass es initialisiert wurde und in sys.modules gespeichert ist.

Sie können dann wie folgt auf die Attribute des Moduls (d.h. auf jeden im Modul definierten Namen) zugreifen:

attr = PyObject_GetAttrString(module, "<attrname>");

Das Aufrufen von PyObject_SetAttrString(), um Variablen im Modul zuzuweisen, funktioniert ebenfalls.

Wie verbinde ich C++-Objekte aus Python?

Abhängig von Ihren Anforderungen gibt es viele Ansätze. Um dies manuell zu tun, lesen Sie zunächst das Dokument „Erweitern und Einbetten“. Bedenken Sie, dass es für das Python-Laufzeitsystem nicht viel Unterschied zwischen C und C++ gibt – daher funktioniert die Strategie, einen neuen Python-Typ um einen C-Struktur- (Zeiger-) Typ aufzubauen, auch für C++-Objekte.

Für C++-Bibliotheken siehe C schreiben ist schwierig; gibt es Alternativen?.

Ich habe ein Modul mit der Setup-Datei hinzugefügt und make schlägt fehl; warum?

Setup muss mit einem Zeilenumbruch enden. Wenn kein Zeilenumbruch vorhanden ist, schlägt der Build-Prozess fehl. (Dies zu beheben erfordert einige hässliche Shell-Skript-Hacks, und dieser Fehler ist so geringfügig, dass er die Mühe nicht wert zu sein scheint.)

Wie debugge ich eine Erweiterung?

Wenn Sie GDB mit dynamisch geladenen Erweiterungen verwenden, können Sie keinen Breakpoint in Ihrer Erweiterung setzen, bis Ihre Erweiterung geladen ist.

Fügen Sie in Ihrer .gdbinit-Datei (oder interaktiv) den Befehl hinzu:

br _PyImport_LoadDynamicModule

Dann, wenn Sie GDB ausführen:

$ gdb /local/bin/python
gdb) run myscript.py
gdb) continue # repeat until your extension is loaded
gdb) finish   # so that your extension is loaded
gdb) br myfunction.c:50
gdb) continue

Ich möchte ein Python-Modul auf meinem Linux-System kompilieren, aber einige Dateien fehlen. Warum?

Die meisten gepackten Versionen von Python lassen einige Dateien weg, die für die Kompilierung von Python-Erweiterungen benötigt werden.

Für Red Hat installieren Sie das python3-devel RPM, um die notwendigen Dateien zu erhalten.

Für Debian führen Sie apt-get install python3-dev aus.

Wie unterscheide ich „unvollständige Eingabe“ von „ungültiger Eingabe“?

Manchmal möchten Sie das Verhalten des Python-Interaktivinterpreters emulieren, bei dem er Ihnen eine Fortsetzungsaufforderung gibt, wenn die Eingabe unvollständig ist (z.B. Sie haben den Anfang einer „if“-Anweisung getippt oder Ihre Klammern oder dreifachen String-Anführungszeichen nicht geschlossen), aber sofort eine Syntaxfehlermeldung ausgibt, wenn die Eingabe ungültig ist.

In Python können Sie das Modul codeop verwenden, das das Verhalten des Parsers ausreichend annähert. IDLE verwendet dies zum Beispiel.

Der einfachste Weg, dies in C zu tun, ist, PyRun_InteractiveLoop() aufzurufen (vielleicht in einem separaten Thread) und den Python-Interpreter die Eingabe für Sie verarbeiten zu lassen. Sie können auch PyOS_ReadlineFunctionPointer() auf Ihre benutzerdefinierte Eingabefunktion verweisen lassen. Siehe Modules/readline.c und Parser/myreadline.c für weitere Hinweise.

Wie finde ich undefinierte g++-Symbole __builtin_new oder __pure_virtual?

Um g++-Erweiterungsmodule dynamisch zu laden, müssen Sie Python neu kompilieren, es mit g++ neu verlinken (ändern Sie LINKCC in der Python Modules Makefile) und Ihr Erweiterungsmodul mit g++ linken (z.B. g++ -shared -o mymodule.so mymodule.o).

Kann ich eine Objektklasse erstellen, deren Methoden teilweise in C und teilweise in Python implementiert sind (z.B. durch Vererbung)?

Ja, Sie können von eingebauten Klassen wie int, list, dict usw. erben.

Die Boost Python Library (BPL, https://www.boost.org/libs/python/doc/index.html) bietet eine Möglichkeit, dies aus C++ zu tun (d.h. Sie können von einer in C++ geschriebenen Erweiterungsklasse unter Verwendung von BPL erben).