FAQ zur Erweiterung/Einbettung

Kann ich eigene Funktionen in C erstellen?

Ja, du kannst in C integrierte Module erstellen, die Funktionen, Variablen, Ausnahmen und sogar neue Typen enthalten. Das wird im Dokument Extending and Embedding the Python Interpreter erklärt.

Die meisten Python-Bücher für Mittelstufe und Fortgeschrittene behandeln dieses Thema ebenfalls.

Kann ich eigene Funktionen in C++ erstellen?

Ja, über die C-Kompatibilitätsmittel von C++. Setze extern "C" { ... } um die Include-Dateien von Python und stelle extern "C" vor jede Funktion, die vom Python-Interpreter aufgerufen wird. Globale oder statische C++-Objekte mit Konstruktoren sind wahrscheinlich keine gute Idee.

C zu schreiben ist mühsam. Gibt es Alternativen?

Es gibt eine Reihe von Alternativen dazu, eigene C-Erweiterungen zu schreiben, je nachdem, was du vorhast. Die empfohlenen Werkzeuge von Dritten bieten sowohl einfachere als auch anspruchsvollere Wege, C- und C++-Erweiterungen für Python zu erstellen.

Wie führe ich beliebige Python-Anweisungen aus C heraus aus?

Die Funktion auf höchster Ebene dafür ist PyRun_SimpleString(). Sie nimmt eine einzelne Zeichenkette, die im Zusammenhang des Moduls __main__ ausgeführt wird, und gibt 0 bei Erfolg und -1 zurück, wenn eine Ausnahme aufgetreten ist (einschließlich SyntaxError). Willst du mehr Kontrolle, verwende PyRun_String(). Sieh dir dazu den Quellcode von PyRun_SimpleString() in Python/pythonrun.c an.

Wie werte ich einen beliebigen Python-Ausdruck aus C heraus aus?

Rufe die Funktion PyRun_String() aus der vorherigen Frage mit dem Startsymbol Py_eval_input auf. Sie zerlegt einen Ausdruck, wertet ihn aus und gibt seinen Wert zurück.

Wie hole ich C-Werte aus einem Python-Objekt heraus?

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

Bei bytes gibt PyBytes_Size() die Länge zurück und PyBytes_AsStringAndSize() liefert einen Zeiger auf den Wert sowie dessen Länge. Beachte, dass bytes-Objekte in Python Nullbytes enthalten können, weshalb strlen() aus C nicht verwendet werden sollte.

Um den Typ eines Objekts zu prüfen, stelle zuerst sicher, dass es nicht NULL ist, und verwende dann PyBytes_Check(), PyTuple_Check(), PyList_Check() und so weiter.

Es gibt außerdem eine API auf höherer Ebene für Python-Objekte, die von der sogenannten ‚abstrakten‘ Schnittstelle bereitgestellt wird – Einzelheiten dazu stehen in Include/abstract.h. Sie erlaubt es, mit jeder Art von Python-Sequenz über Aufrufe wie PySequence_Length() und PySequence_GetItem() zu arbeiten, und bietet viele weitere nützliche Protokolle, etwa für Zahlen (PyNumber_Index() und Verwandte) und für Mappings in den PyMapping-APIs.

Wie erzeuge ich mit Py_BuildValue() ein Tupel beliebiger Länge?

Das geht nicht. Verwende stattdessen PyTuple_Pack().

Wie rufe ich die Methode eines Objekts aus C heraus auf?

Mit der Funktion PyObject_CallMethod() lässt sich eine beliebige Methode eines Objekts aufrufen. Die Parameter sind das Objekt, der Name der aufzurufenden Methode, eine Formatzeichenkette wie bei Py_BuildValue() und die Argumentwerte:

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

Das funktioniert für jedes Objekt, das Methoden hat – egal ob integriert oder selbst definiert. Du bist dafür verantwortlich, den Rückgabewert irgendwann mit Py_DECREF()‚ing freizugeben.

Um zum Beispiel die Methode „seek“ eines Dateiobjekts mit den Argumenten 10, 0 aufzurufen (angenommen, der Zeiger auf das Dateiobjekt heißt „f“):

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

Beachte: Da PyObject_CallObject() als Argumentliste immer ein Tupel erwartet, übergib für einen Aufruf ohne Argumente „()“ als Format, und setze bei einem Aufruf mit einem Argument das Argument in Klammern, also etwa „(i)“.

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

Lege in Python-Code ein Objekt an, das die Methode write() unterstützt. Weise dieses Objekt sys.stdout und sys.stderr zu. Rufe print_error auf oder lass einfach den üblichen Traceback-Mechanismus arbeiten. Die Ausgabe geht dann dorthin, wohin deine Methode write() sie schickt.

Am einfachsten geht das mit der Klasse io.StringIO:

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

Ein eigenes Objekt, das dasselbe tut, sähe so aus:

>>> 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 aus C auf ein in Python geschriebenes Modul zu?

Einen Zeiger auf das Modulobjekt bekommst du so:

module = PyImport_ImportModule("<modulename>");

Wurde das Modul noch nicht importiert, ist also noch nicht in sys.modules vorhanden, initialisiert dies das Modul. Andernfalls gibt es einfach den Wert von sys.modules["<modulename>"] zurück. Beachte, dass es das Modul in keinen Namensraum einträgt – es stellt nur sicher, dass das Modul initialisiert wurde und in sys.modules liegt.

Auf die Attribute des Moduls, also auf jeden im Modul festgelegten Namen, greifst du dann so zu:

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

PyObject_SetAttrString() aufzurufen, um Variablen im Modul zuzuweisen, funktioniert ebenfalls.

Wie spreche ich C++-Objekte aus Python heraus an?

Je nach Anforderungen gibt es viele Wege. Um es von Hand zu machen, lies zunächst das Dokument „Extending and Embedding“. Mach dir klar, dass es für das Laufzeitsystem von Python keinen großen Unterschied zwischen C und C++ gibt – die Strategie, einen neuen Python-Typ um einen C-Strukturtyp (beziehungsweise Zeiger darauf) zu bauen, funktioniert deshalb auch für C++-Objekte.

Für C++-Bibliotheken siehe C zu schreiben ist mühsam. Gibt es Alternativen?.

Ich habe mit der Setup-Datei ein Modul hinzugefügt, und make schlägt fehl. Warum?

Setup muss mit einem Zeilenumbruch enden. Fehlt der Zeilenumbruch, scheitert der Bauvorgang. (Das zu beheben würde einige hässliche Verrenkungen im Shell-Skript erfordern, und dieser Fehler ist so geringfügig, dass es den Aufwand nicht wert zu sein scheint.)

Wie finde ich Fehler in einer Erweiterung?

Verwendest du GDB mit dynamisch geladenen Erweiterungen, kannst du in deiner Erweiterung keinen Haltepunkt setzen, bevor sie geladen ist.

Füge in deiner Datei .gdbinit (oder interaktiv) diesen Befehl hinzu:

br _PyImport_LoadDynamicModule

Und dann, wenn du GDB ausführst:

$ gdb /local/bin/python
gdb) run myscript.py
gdb) continue # wiederholen, bis deine Erweiterung geladen ist
gdb) finish   # damit deine Erweiterung geladen wird
gdb) br myfunction.c:50
gdb) continue

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

Den meisten paketierten Fassungen von Python fehlen einige Dateien, die zum Kompilieren von Python-Erweiterungen nötig sind.

Installiere unter Red Hat das RPM python3-devel, um die nötigen Dateien zu bekommen.

Führe unter Debian apt-get install python3-dev aus.

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

Manchmal willst du das Verhalten des interaktiven Python-Interpreters nachbilden, der dir eine Eingabeaufforderung zur Fortsetzung zeigt, wenn die Eingabe unvollständig ist (etwa wenn du den Anfang einer „if“-Anweisung getippt hast oder deine Klammern beziehungsweise dreifachen Anführungszeichen nicht geschlossen hast), der dir aber sofort eine Syntaxfehlermeldung gibt, wenn die Eingabe ungültig ist.

In Python kannst du das Modul codeop verwenden, das das Verhalten des Parsers hinreichend annähert. IDLE verwendet das zum Beispiel.

In C geht es am einfachsten, indem du PyRun_InteractiveLoop() aufrufst, vielleicht in einem eigenen Thread, und den Python-Interpreter die Eingabe für dich erledigen lässt. Du kannst auch PyOS_ReadlineFunctionPointer() auf deine eigene Eingabefunktion zeigen lassen. Weitere Hinweise stehen in Modules/readline.c und Parser/myreadline.c.

Wie finde ich die undefinierten g++-Symbole __builtin_new oder __pure_virtual?

Um Erweiterungsmodule von g++ dynamisch zu laden, musst du Python neu kompilieren, es mit g++ neu binden (ändere LINKCC im Makefile der Python-Module) und dein Erweiterungsmodul mit g++ binden, zum Beispiel mit g++ -shared -o mymodule.so mymodule.o.

Kann ich eine Objektklasse erzeugen, bei der manche Methoden in C und andere in Python umgesetzt sind, etwa über Vererbung?

Ja, du kannst von integrierten Klassen wie int, list, dict und so weiter erben.

Die Boost Python Library (BPL, https://www.boost.org/libs/python/doc/index.html) bietet einen Weg, das von C++ aus zu tun: Du kannst von einer Erweiterungsklasse erben, die mit der BPL in C++ geschrieben ist.