1. Erweitern von Python mit C oder C++¶
Es ist ziemlich einfach, neue eingebaute Module zu Python hinzuzufügen, wenn man weiß, wie man in C programmiert. Solche Erweiterungsmodule können zwei Dinge tun, die in Python nicht direkt möglich sind: Sie können neue eingebaute Objekttypen implementieren und sie können C-Bibliotheksfunktionen und Systemaufrufe aufrufen.
Zur Unterstützung von Erweiterungen definiert die Python-API (Application Programmers Interface) eine Reihe von Funktionen, Makros und Variablen, die Zugriff auf die meisten Aspekte des Python-Laufzeitsystems bieten. Die Python-API wird durch die Einbindung der Header-Datei "Python.h" in eine C-Quelldatei eingebunden.
Die Kompilierung eines Erweiterungsmoduls hängt von seinem beabsichtigten Verwendungszweck sowie von Ihrer Systemkonfiguration ab; Details werden in späteren Kapiteln erläutert.
Hinweis
Die C-Erweiterungsschnittstelle ist spezifisch für CPython, und Erweiterungsmodule funktionieren nicht auf anderen Python-Implementierungen. In vielen Fällen ist es möglich, das Schreiben von C-Erweiterungen zu vermeiden und die Portabilität zu anderen Implementierungen zu erhalten. Wenn Ihr Anwendungsfall beispielsweise das Aufrufen von C-Bibliotheksfunktionen oder Systemaufrufen ist, sollten Sie die Verwendung des Moduls ctypes oder der cffi-Bibliothek in Betracht ziehen, anstatt benutzerdefinierten C-Code zu schreiben. Diese Module ermöglichen es Ihnen, Python-Code zu schreiben, um mit C-Code zu interagieren, und sind zwischen Python-Implementierungen portabler als das Schreiben und Kompilieren eines C-Erweiterungsmoduls.
1.1. Ein einfaches Beispiel¶
Lassen Sie uns ein Erweiterungsmodul namens spam (das Lieblingsessen von Monty-Python-Fans…) erstellen und sagen wir, wir möchten eine Python-Schnittstelle zur C-Bibliotheksfunktion system() erstellen [1]. Diese Funktion nimmt einen nullterminierten Zeichenketten als Argument und gibt einen Integer zurück. Wir möchten, dass diese Funktion wie folgt von Python aufgerufen werden kann:
>>> import spam
>>> status = spam.system("ls -l")
Beginnen Sie mit der Erstellung einer Datei spammodule.c. (Historisch gesehen, wenn ein Modul spam heißt, heißt die C-Datei, die seine Implementierung enthält, spammodule.c; wenn der Modulname sehr lang ist, wie spammify, kann der Modulname einfach spammify.c sein.)
Die ersten beiden Zeilen unserer Datei können lauten:
#define PY_SSIZE_T_CLEAN
#include <Python.h>
Dies lädt die Python-API ein (Sie können bei Bedarf einen Kommentar hinzufügen, der den Zweck des Moduls beschreibt, sowie einen Copyright-Hinweis).
Hinweis
Da Python einige Präprozessor-Definitionen definieren kann, die die Standard-Header auf einigen Systemen beeinflussen, *müssen* Sie Python.h einbinden, bevor irgendwelche Standard-Header eingebunden werden.
#define PY_SSIZE_T_CLEAN wurde verwendet, um anzuzeigen, dass Py_ssize_t in einigen APIs anstelle von int verwendet werden sollte. Es ist seit Python 3.13 nicht mehr notwendig, aber wir behalten es hier aus Kompatibilitätsgründen bei. Siehe Strings und Puffer für eine Beschreibung dieses Makros.
Alle von Python.h definierten, für Benutzer sichtbaren Symbole haben, mit Ausnahme der in Standard-Headerdateien definierten, das Präfix Py oder PY.
Tipp
Aus Kompatibilitätsgründen bindet Python.h mehrere Standard-Headerdateien ein. C-Erweiterungen sollten die von ihnen verwendeten Standard-Header einbinden und sich nicht auf diese impliziten Einbindungen verlassen. Wenn die begrenzte C-API Version 3.13 oder neuer verwendet wird, sind die impliziten Einbindungen:
<assert.h><intrin.h>(unter Windows)<inttypes.h><limits.h><math.h><stdarg.h><wchar.h><sys/types.h>(falls vorhanden)
Wenn Py_LIMITED_API nicht definiert ist oder auf Version 3.12 oder älter gesetzt ist, werden auch die folgenden Header eingebunden:
<ctype.h><unistd.h>(auf POSIX)
Wenn Py_LIMITED_API nicht definiert ist oder auf Version 3.10 oder älter gesetzt ist, werden auch die folgenden Header eingebunden:
<errno.h><stdio.h><stdlib.h><string.h>
Als Nächstes fügen wir unserer Moduldatei die C-Funktion hinzu, die aufgerufen wird, wenn der Python-Ausdruck spam.system(string) ausgewertet wird (wir werden gleich sehen, wie sie aufgerufen wird):
static PyObject *
spam_system(PyObject *self, PyObject *args)
{
const char *command;
int sts;
if (!PyArg_ParseTuple(args, "s", &command))
return NULL;
sts = system(command);
return PyLong_FromLong(sts);
}
Es gibt eine direkte Übersetzung von der Argumentliste in Python (z. B. dem einzelnen Ausdruck "ls -l") zu den Argumenten, die an die C-Funktion übergeben werden. Die C-Funktion hat immer zwei Argumente, die konventionell *self* und *args* genannt werden.
Das *self*-Argument zeigt auf das Modulobjekt für Funktionen auf Modulebene; für eine Methode würde es auf die Objektinstanz zeigen.
Das *args*-Argument ist ein Zeiger auf ein Python-Tuple-Objekt, das die Argumente enthält. Jedes Element des Tupels entspricht einem Argument in der Argumentliste des Aufrufs. Die Argumente sind Python-Objekte – um etwas mit ihnen in unserer C-Funktion zu tun, müssen wir sie in C-Werte umwandeln. Die Funktion PyArg_ParseTuple() in der Python-API prüft die Argumenttypen und wandelt sie in C-Werte um. Sie verwendet eine Vorlagenzeichenkette, um die erforderlichen Typen der Argumente sowie die Typen der C-Variablen zu bestimmen, in denen die konvertierten Werte gespeichert werden. Mehr dazu später.
PyArg_ParseTuple() gibt wahr (ungleich null) zurück, wenn alle Argumente den richtigen Typ haben und ihre Komponenten in den Variablen gespeichert wurden, deren Adressen übergeben werden. Sie gibt falsch (null) zurück, wenn eine ungültige Argumentliste übergeben wurde. In letzterem Fall wird auch eine entsprechende Ausnahme ausgelöst, sodass die aufrufende Funktion sofort NULL zurückgeben kann (wie wir im Beispiel gesehen haben).
1.2. Zwischenspiel: Fehler und Ausnahmen¶
Eine wichtige Konvention im gesamten Python-Interpreter ist die folgende: Wenn eine Funktion fehlschlägt, sollte sie eine Ausnahmebedingung setzen und einen Fehlerwert zurückgeben (normalerweise -1 oder ein NULL-Zeiger). Ausnahminformationen werden in drei Mitgliedern des Thread-Zustands des Interpreters gespeichert. Dies sind NULL, wenn keine Ausnahme vorliegt. Andernfalls sind sie die C-Entsprechungen der Mitglieder des Python-Tupels, das von sys.exc_info() zurückgegeben wird. Dies sind der Ausnahmetyp, die Ausnahmeinstanz und ein Traceback-Objekt. Es ist wichtig, diese zu kennen, um zu verstehen, wie Fehler weitergegeben werden.
Die Python-API definiert eine Reihe von Funktionen zum Setzen verschiedener Ausnahmetypen.
Die gebräuchlichste ist PyErr_SetString(). Ihre Argumente sind ein Ausnahmeobjekt und eine C-Zeichenkette. Das Ausnahmeobjekt ist normalerweise ein vordefiniertes Objekt wie PyExc_ZeroDivisionError. Die C-Zeichenkette gibt die Ursache des Fehlers an und wird in ein Python-String-Objekt umgewandelt und als „zugehöriger Wert“ der Ausnahme gespeichert.
Eine weitere nützliche Funktion ist PyErr_SetFromErrno(), die nur ein Ausnahmeargument nimmt und den zugehörigen Wert durch Inspektion der globalen Variablen errno konstruiert. Die allgemeinste Funktion ist PyErr_SetObject(), die zwei Objektargumente nimmt, die Ausnahme und ihren zugehörigen Wert. Sie müssen die an diese Funktionen übergebenen Objekte nicht mit Py_INCREF() referenzieren.
Sie können nicht-zerstörend testen, ob eine Ausnahme gesetzt wurde, mit PyErr_Occurred(). Diese gibt das aktuelle Ausnahmeobjekt oder NULL zurück, wenn keine Ausnahme aufgetreten ist. Sie müssen normalerweise PyErr_Occurred() nicht aufrufen, um zu sehen, ob ein Fehler bei einem Funktionsaufruf aufgetreten ist, da Sie dies aus dem Rückgabewert ableiten können.
Wenn eine Funktion *f*, die eine andere Funktion *g* aufruft, feststellt, dass letztere fehlschlägt, sollte *f* selbst einen Fehlerwert zurückgeben (normalerweise NULL oder -1). Sie sollte *keine* der PyErr_*-Funktionen aufrufen – eine wurde bereits von *g* aufgerufen. Der Aufrufer von *f* soll dann ebenfalls eine Fehleranzeige an seinen Aufrufer zurückgeben, wiederum *ohne* PyErr_* aufzurufen, und so weiter – die detaillierteste Ursache des Fehlers wurde bereits von der Funktion gemeldet, die sie zuerst entdeckt hat. Sobald der Fehler die Hauptschleife des Python-Interpreters erreicht, bricht dieser den aktuell ausgeführten Python-Code ab und versucht, einen vom Python-Programmierer angegebenen Ausnahmebehandler zu finden.
(Es gibt Situationen, in denen ein Modul tatsächlich eine detailliertere Fehlermeldung geben kann, indem es eine andere PyErr_*-Funktion aufruft, und in solchen Fällen ist es in Ordnung, dies zu tun. Als allgemeine Regel ist dies jedoch nicht notwendig und kann dazu führen, dass Informationen über die Ursache des Fehlers verloren gehen: Die meisten Operationen können aus verschiedenen Gründen fehlschlagen.)
Um eine durch einen fehlgeschlagenen Funktionsaufruf gesetzte Ausnahme zu ignorieren, muss die Ausnahmebedingung explizit durch Aufrufen von PyErr_Clear() gelöscht werden. Der einzige Zeitpunkt, zu dem C-Code PyErr_Clear() aufrufen sollte, ist, wenn er den Fehler nicht an den Interpreter weitergeben möchte, sondern ihn vollständig selbst behandeln will (möglicherweise durch Versuch eines anderen Ansatzes oder so tun, als wäre nichts passiert).
Jeder fehlerhafte malloc()-Aufruf muss in eine Ausnahme umgewandelt werden – der direkte Aufrufer von malloc() (oder realloc()) muss PyErr_NoMemory() aufrufen und selbst einen Fehlerindikator zurückgeben. Alle Objekt-erzeugenden Funktionen (z. B. PyLong_FromLong()) tun dies bereits, daher ist dieser Hinweis nur für diejenigen relevant, die malloc() direkt aufrufen.
Beachten Sie auch, dass Funktionen, die einen Integer-Status zurückgeben, mit der wichtigen Ausnahme von PyArg_ParseTuple() und ähnlichen Funktionen einen positiven Wert oder null für Erfolg und -1 für Fehler zurückgeben, ähnlich wie bei Unix-Systemaufrufen.
Achten Sie schließlich darauf, Müll aufzuräumen (indem Sie Py_XDECREF() oder Py_DECREF() Aufrufe für bereits erstellte Objekte machen), wenn Sie einen Fehlerindikator zurückgeben!
Die Wahl, welche Ausnahme ausgelöst werden soll, liegt ganz bei Ihnen. Es gibt vordefinierte C-Objekte, die allen integrierten Python-Ausnahmen entsprechen, wie z. B. PyExc_ZeroDivisionError, die Sie direkt verwenden können. Natürlich sollten Sie Ausnahmen mit Bedacht wählen – verwenden Sie nicht PyExc_TypeError, um auszudrücken, dass eine Datei nicht geöffnet werden konnte (das sollte wahrscheinlich PyExc_OSError sein). Wenn etwas mit der Argumentliste nicht stimmt, löst die Funktion PyArg_ParseTuple() normalerweise PyExc_TypeError aus. Wenn Sie ein Argument haben, dessen Wert in einem bestimmten Bereich liegen oder andere Bedingungen erfüllen muss, ist PyExc_ValueError angemessen.
Sie können auch eine neue Ausnahme definieren, die einzigartig für Ihr Modul ist. Der einfachste Weg, dies zu tun, ist die Deklaration einer statischen globalen Objektvariable am Anfang der Datei:
static PyObject *SpamError = NULL;
und initialisieren Sie sie, indem Sie PyErr_NewException() in der Py_mod_exec-Funktion des Moduls aufrufen (z. B. spam_module_exec()):
SpamError = PyErr_NewException("spam.error", NULL, NULL);
Da SpamError eine globale Variable ist, wird sie bei jeder Neuinitialisierung des Moduls überschrieben, wenn die Funktion Py_mod_exec aufgerufen wird.
Vorerst vermeiden wir das Problem: Wir blockieren die wiederholte Initialisierung, indem wir einen ImportError auslösen:
static PyObject *SpamError = NULL;
static int
spam_module_exec(PyObject *m)
{
if (SpamError != NULL) {
PyErr_SetString(PyExc_ImportError,
"cannot initialize spam module more than once");
return -1;
}
SpamError = PyErr_NewException("spam.error", NULL, NULL);
if (PyModule_AddObjectRef(m, "SpamError", SpamError) < 0) {
return -1;
}
return 0;
}
static PyModuleDef_Slot spam_module_slots[] = {
{Py_mod_exec, spam_module_exec},
{0, NULL}
};
static struct PyModuleDef spam_module = {
.m_base = PyModuleDef_HEAD_INIT,
.m_name = "spam",
.m_size = 0, // non-negative
.m_slots = spam_module_slots,
};
PyMODINIT_FUNC
PyInit_spam(void)
{
return PyModuleDef_Init(&spam_module);
}
Beachten Sie, dass der Python-Name für das Ausnahmeobjekt spam.error ist. Die Funktion PyErr_NewException() kann eine Klasse mit der Basisklasse Exception erstellen (es sei denn, anstelle von NULL wird eine andere Klasse übergeben), wie in Eingebaute Ausnahmen beschrieben.
Beachten Sie auch, dass die Variable SpamError eine Referenz auf die neu erstellte Ausnahmeklasse behält; das ist beabsichtigt! Da die Ausnahme durch externen Code aus dem Modul entfernt werden könnte, wird eine besessene Referenz auf die Klasse benötigt, um sicherzustellen, dass sie nicht verworfen wird, was dazu führt, dass SpamError zu einem hängenden Zeiger wird. Sollte sie zu einem hängenden Zeiger werden, könnte C-Code, der die Ausnahme auslöst, zu einem Core-Dump oder anderen unbeabsichtigten Nebeneffekten führen.
Vorerst fehlt der Aufruf von Py_DECREF(), um diese Referenz zu entfernen. Selbst wenn der Python-Interpreter herunterfährt, wird die globale Variable SpamError nicht vom Garbage Collector gesammelt. Sie wird „lecken“. Wir haben jedoch sichergestellt, dass dies höchstens einmal pro Prozess geschieht.
Wir werden später in diesem Beispiel auf die Verwendung von PyMODINIT_FUNC als Rückgabetyp einer Funktion eingehen.
Die Ausnahme spam.error kann in Ihrem Erweiterungsmodul mit einem Aufruf von PyErr_SetString() ausgelöst werden, wie unten gezeigt:
static PyObject *
spam_system(PyObject *self, PyObject *args)
{
const char *command;
int sts;
if (!PyArg_ParseTuple(args, "s", &command))
return NULL;
sts = system(command);
if (sts < 0) {
PyErr_SetString(SpamError, "System command failed");
return NULL;
}
return PyLong_FromLong(sts);
}
1.3. Zurück zum Beispiel¶
Zurück zu unserer Beispiel-Funktion sollten Sie nun diese Anweisung verstehen können:
if (!PyArg_ParseTuple(args, "s", &command))
return NULL;
Sie gibt NULL zurück (den Fehlerindikator für Funktionen, die Objektzeiger zurückgeben), wenn in der Argumentliste ein Fehler erkannt wird, und verlässt sich dabei auf die von PyArg_ParseTuple() gesetzte Ausnahme. Andernfalls wurde der Zeichenkettenwert des Arguments in die lokale Variable command kopiert. Dies ist eine Zeigerzuweisung und Sie sollten die Zeichenkette, auf die sie zeigt, nicht ändern (daher sollte die Variable command in Standard-C korrekt als const char *command deklariert werden).
Die nächste Anweisung ist ein Aufruf der Unix-Funktion system(), der die Zeichenkette übergeben wird, die wir gerade von PyArg_ParseTuple() erhalten haben:
sts = system(command);
Unsere spam.system()-Funktion muss den Wert von sts als Python-Objekt zurückgeben. Dies geschieht mit der Funktion PyLong_FromLong().
return PyLong_FromLong(sts);
In diesem Fall wird ein Integer-Objekt zurückgegeben. (Ja, selbst Integer sind in Python Objekte auf dem Heap!)
Wenn Sie eine C-Funktion haben, die kein nützliches Argument zurückgibt (eine Funktion, die void zurückgibt), muss die entsprechende Python-Funktion None zurückgeben. Sie benötigen dieses Idiom, um dies zu tun (was durch das Makro Py_RETURN_NONE implementiert wird):
Py_INCREF(Py_None);
return Py_None;
Py_None ist der C-Name für das spezielle Python-Objekt None. Es ist ein echtes Python-Objekt und kein NULL-Zeiger, der in den meisten Kontexten „Fehler“ bedeutet, wie wir gesehen haben.
1.4. Die Methodentabelle und Initialisierungsfunktion des Moduls¶
Ich habe versprochen zu zeigen, wie spam_system() aus Python-Programmen aufgerufen wird. Zuerst müssen wir seinen Namen und seine Adresse in einer „Methodentabelle“ auflisten:
static PyMethodDef spam_methods[] = {
...
{"system", spam_system, METH_VARARGS,
"Execute a shell command."},
...
{NULL, NULL, 0, NULL} /* Sentinel */
};
Beachten Sie den dritten Eintrag (METH_VARARGS). Dies ist ein Flag, das dem Interpreter die Aufrufkonvention für die C-Funktion mitteilt. Es sollte normalerweise immer METH_VARARGS oder METH_VARARGS | METH_KEYWORDS sein; ein Wert von 0 bedeutet, dass eine veraltete Variante von PyArg_ParseTuple() verwendet wird.
Wenn nur METH_VARARGS verwendet wird, sollte die Funktion die Parameter auf Python-Ebene als Tupel erwarten, das über PyArg_ParseTuple() analysiert werden kann; weitere Informationen zu dieser Funktion finden Sie weiter unten.
Das Bit METH_KEYWORDS kann im dritten Feld gesetzt werden, wenn Schlüsselwortargumente an die Funktion übergeben werden sollen. In diesem Fall sollte die C-Funktion einen dritten Parameter vom Typ PyObject * akzeptieren, der ein Wörterbuch von Schlüsselwörtern sein wird. Verwenden Sie PyArg_ParseTupleAndKeywords(), um die Argumente einer solchen Funktion zu analysieren.
Die Methodentabelle muss in der Moduldefinitionsstruktur referenziert werden:
static struct PyModuleDef spam_module = {
...
.m_methods = spam_methods,
...
};
Diese Struktur muss wiederum dem Interpreter in der Initialisierungsfunktion des Moduls übergeben werden. Die Initialisierungsfunktion muss PyInit_name() heißen, wobei *name* der Name des Moduls ist, und sollte das einzige nicht-statice Element sein, das in der Moduldatei definiert ist:
PyMODINIT_FUNC
PyInit_spam(void)
{
return PyModuleDef_Init(&spam_module);
}
Beachten Sie, dass PyMODINIT_FUNC die Funktion mit dem Rückgabetyp PyObject * deklariert, die erforderlichen plattformspezifischen Linkage-Deklarationen deklariert und für C++ die Funktion als extern "C" deklariert.
PyInit_spam() wird aufgerufen, wenn jeder Interpreter sein Modul spam zum ersten Mal importiert. (Siehe unten für Kommentare zum Einbetten von Python.) Ein Zeiger auf die Moduldefinition muss über PyModuleDef_Init() zurückgegeben werden, damit die Import-Maschinerie das Modul erstellen und in sys.modules speichern kann.
Beim Einbetten von Python wird die Funktion PyInit_spam() nicht automatisch aufgerufen, es sei denn, es gibt einen Eintrag in der PyImport_Inittab-Tabelle. Um das Modul zur Initialisierungstabelle hinzuzufügen, verwenden Sie PyImport_AppendInittab(), optional gefolgt von einem Import des Moduls:
#define PY_SSIZE_T_CLEAN
#include <Python.h>
int
main(int argc, char *argv[])
{
PyStatus status;
PyConfig config;
PyConfig_InitPythonConfig(&config);
/* Add a built-in module, before Py_Initialize */
if (PyImport_AppendInittab("spam", PyInit_spam) == -1) {
fprintf(stderr, "Error: could not extend in-built modules table\n");
exit(1);
}
/* Pass argv[0] to the Python interpreter */
status = PyConfig_SetBytesString(&config, &config.program_name, argv[0]);
if (PyStatus_Exception(status)) {
goto exception;
}
/* Initialize the Python interpreter. Required.
If this step fails, it will be a fatal error. */
status = Py_InitializeFromConfig(&config);
if (PyStatus_Exception(status)) {
goto exception;
}
PyConfig_Clear(&config);
/* Optionally import the module; alternatively,
import can be deferred until the embedded script
imports it. */
PyObject *pmodule = PyImport_ImportModule("spam");
if (!pmodule) {
PyErr_Print();
fprintf(stderr, "Error: could not import module 'spam'\n");
}
// ... use Python C API here ...
return 0;
exception:
PyConfig_Clear(&config);
Py_ExitStatusException(status);
}
Hinweis
Wenn Sie eine globale Variable oder eine lokale statische Variable deklarieren, kann das Modul unbeabsichtigte Nebeneffekte bei der Reinitialisierung erfahren, z. B. beim Entfernen von Einträgen aus sys.modules oder beim Importieren kompilierter Module in mehrere Interpreter innerhalb eines Prozesses (oder nach einem fork() ohne dazwischenliegendes exec()). Wenn der Modulzustand noch nicht vollständig isoliert ist, sollten Autoren in Erwägung ziehen, das Modul als nicht unterstützend für Sub-Interpreter zu markieren (über Py_MOD_MULTIPLE_INTERPRETERS_NOT_SUPPORTED).
Ein umfangreicheres Beispielmodul ist in der Python-Quellcode-Distribution als Modules/xxlimited.c enthalten. Diese Datei kann als Vorlage dienen oder einfach als Beispiel gelesen werden.
1.5. Kompilierung und Verknüpfung¶
Es gibt noch zwei Dinge zu tun, bevor Sie Ihre neue Erweiterung verwenden können: sie kompilieren und mit dem Python-System verknüpfen. Wenn Sie dynamisches Laden verwenden, können die Details vom Stil des dynamischen Ladens Ihres Systems abhängen; siehe die Kapitel über das Erstellen von Erweiterungsmodulen (Kapitel Erstellen von C- und C++-Erweiterungen) und zusätzliche Informationen, die nur für das Erstellen unter Windows gelten (Kapitel Erstellen von C- und C++-Erweiterungen unter Windows) für weitere Informationen hierzu.
Wenn Sie kein dynamisches Laden verwenden können oder wenn Ihr Modul ein permanenter Bestandteil des Python-Interpreters werden soll, müssen Sie die Konfiguration ändern und den Interpreter neu kompilieren. Glücklicherweise ist dies unter Unix sehr einfach: platzieren Sie einfach Ihre Datei (z. B. spammodule.c) im Verzeichnis Modules/ einer entpackten Quellcode-Distribution, fügen Sie eine Zeile zur Datei Modules/Setup.local hinzu, die Ihre Datei beschreibt:
spam spammodule.o
und kompilieren Sie den Interpreter neu, indem Sie im obersten Verzeichnis make ausführen. Sie können auch make im Unterverzeichnis Modules/ ausführen, müssen aber zuerst Makefile dort neu erstellen, indem Sie ‘make Makefile’ ausführen. (Dies ist jedes Mal erforderlich, wenn Sie die Setup-Datei ändern.)
Wenn Ihr Modul zusätzliche Bibliotheken zum Verknüpfen benötigt, können diese ebenfalls auf der Zeile in der Konfigurationsdatei aufgeführt werden, z. B.:
spam spammodule.o -lX11
1.6. Aufrufen von Python-Funktionen aus C¶
Bisher haben wir uns darauf konzentriert, C-Funktionen von Python aus aufrufbar zu machen. Das Gegenteil ist ebenfalls nützlich: das Aufrufen von Python-Funktionen aus C. Dies ist besonders bei Bibliotheken der Fall, die sogenannte „Callback“-Funktionen unterstützen. Wenn eine C-Schnittstelle Callbacks verwendet, muss die entsprechende Python-Funktion oft einen Callback-Mechanismus für den Python-Programmierer bereitstellen; die Implementierung erfordert den Aufruf der Python-Callback-Funktionen aus einem C-Callback. Andere Verwendungszwecke sind ebenfalls denkbar.
Glücklicherweise ist der Python-Interpreter leicht rekursiv aufrufbar, und es gibt eine Standard-Schnittstelle zum Aufrufen einer Python-Funktion. (Ich werde nicht darauf eingehen, wie man den Python-Parser mit einer bestimmten Zeichenkette als Eingabe aufruft – wenn Sie interessiert sind, schauen Sie sich die Implementierung der Befehlszeilenoption -c in Modules/main.c aus dem Python-Quellcode an.)
Das Aufrufen einer Python-Funktion ist einfach. Zuerst muss das Python-Programm Ihnen irgendwie das Python-Funktionsobjekt übergeben. Sie sollten eine Funktion (oder eine andere Schnittstelle) bereitstellen, um dies zu tun. Wenn diese Funktion aufgerufen wird, speichern Sie einen Zeiger auf das Python-Funktionsobjekt (achten Sie darauf, Py_INCREF() ihn zu verwenden!) in einer globalen Variablen – oder wo auch immer Sie es für richtig halten. Zum Beispiel könnte die folgende Funktion Teil einer Moduldefinition sein:
static PyObject *my_callback = NULL;
static PyObject *
my_set_callback(PyObject *dummy, PyObject *args)
{
PyObject *result = NULL;
PyObject *temp;
if (PyArg_ParseTuple(args, "O:set_callback", &temp)) {
if (!PyCallable_Check(temp)) {
PyErr_SetString(PyExc_TypeError, "parameter must be callable");
return NULL;
}
Py_XINCREF(temp); /* Add a reference to new callback */
Py_XDECREF(my_callback); /* Dispose of previous callback */
my_callback = temp; /* Remember new callback */
/* Boilerplate to return "None" */
Py_INCREF(Py_None);
result = Py_None;
}
return result;
}
Diese Funktion muss dem Interpreter mit dem Flag METH_VARARGS registriert werden; dies ist in Abschnitt Die Methodentabelle und Initialisierungsfunktion des Moduls beschrieben. Die Funktion PyArg_ParseTuple() und ihre Argumente sind in Abschnitt Parameter in Erweiterungsfunktionen extrahieren dokumentiert.
Die Makros Py_XINCREF() und Py_XDECREF() erhöhen/verringern die Referenzanzahl eines Objekts und sind sicher in Gegenwart von NULL-Zeigern (aber beachten Sie, dass temp in diesem Kontext nicht NULL sein wird). Weitere Informationen dazu finden Sie in Abschnitt Referenzzähler.
Später, wenn es an der Zeit ist, die Funktion aufzurufen, rufen Sie die C-Funktion PyObject_CallObject() auf. Diese Funktion hat zwei Argumente, beides Zeiger auf beliebige Python-Objekte: die Python-Funktion und die Argumentliste. Die Argumentliste muss immer ein Tupel-Objekt sein, dessen Länge der Anzahl der Argumente entspricht. Um die Python-Funktion ohne Argumente aufzurufen, übergeben Sie NULL oder ein leeres Tupel; um sie mit einem Argument aufzurufen, übergeben Sie ein Singleton-Tupel. Py_BuildValue() gibt ein Tupel zurück, wenn seine Formatzeichenkette null oder mehr Formatierungszeichen in Klammern enthält. Zum Beispiel
int arg;
PyObject *arglist;
PyObject *result;
...
arg = 123;
...
/* Time to call the callback */
arglist = Py_BuildValue("(i)", arg);
result = PyObject_CallObject(my_callback, arglist);
Py_DECREF(arglist);
PyObject_CallObject() gibt einen Python-Objektzeiger zurück: Dies ist der Rückgabewert der Python-Funktion. PyObject_CallObject() ist in Bezug auf seine Argumente „referenzzähler-neutral“. Im Beispiel wurde ein neues Tupel als Argumentliste erstellt, das sofort nach dem Aufruf von PyObject_CallObject() mit Py_DECREF() dekrementiert wird.
Der Rückgabewert von PyObject_CallObject() ist „neu“: Entweder ist es ein brandneues Objekt, oder es ist ein bestehendes Objekt, dessen Referenzzähler erhöht wurde. Wenn Sie es also nicht in einer globalen Variablen speichern möchten, sollten Sie das Ergebnis auf irgendeine Weise mit Py_DECREF() dekrementieren, auch (insbesondere!) wenn Sie nicht an seinem Wert interessiert sind.
Bevor Sie dies tun, ist es jedoch wichtig zu prüfen, ob der Rückgabewert nicht NULL ist. Wenn dies der Fall ist, wurde die Python-Funktion durch Auslösen einer Ausnahme beendet. Wenn der C-Code, der PyObject_CallObject() aufgerufen hat, von Python aufgerufen wird, sollte er nun eine Fehleranzeige an seinen Python-Aufrufer zurückgeben, damit der Interpreter eine Stapelverfolgung ausgeben kann oder der aufrufende Python-Code die Ausnahme behandeln kann. Wenn dies nicht möglich oder erwünscht ist, sollte die Ausnahme durch Aufrufen von PyErr_Clear() gelöscht werden. Zum Beispiel
if (result == NULL)
return NULL; /* Pass error back */
...use result...
Py_DECREF(result);
Je nach gewünschter Schnittstelle zur Python-Callback-Funktion müssen Sie möglicherweise auch eine Argumentliste an PyObject_CallObject() übergeben. In einigen Fällen wird die Argumentliste auch vom Python-Programm über dieselbe Schnittstelle bereitgestellt, die die Callback-Funktion spezifiziert hat. Sie kann dann gespeichert und auf dieselbe Weise wie das Funktions-Objekt verwendet werden. In anderen Fällen müssen Sie möglicherweise ein neues Tupel konstruieren, das als Argumentliste übergeben wird. Der einfachste Weg, dies zu tun, ist der Aufruf von Py_BuildValue(). Wenn Sie beispielsweise einen ganzzahligen Ereigniscode übergeben möchten, können Sie den folgenden Code verwenden
PyObject *arglist;
...
arglist = Py_BuildValue("(l)", eventcode);
result = PyObject_CallObject(my_callback, arglist);
Py_DECREF(arglist);
if (result == NULL)
return NULL; /* Pass error back */
/* Here maybe use the result */
Py_DECREF(result);
Beachten Sie die Platzierung von Py_DECREF(arglist) unmittelbar nach dem Aufruf, vor der Fehlerprüfung! Beachten Sie auch, dass dieser Code streng genommen nicht vollständig ist: Py_BuildValue() kann den Speicher erschöpfen, und dies sollte überprüft werden.
Sie können auch eine Funktion mit Schlüsselwortargumenten aufrufen, indem Sie PyObject_Call() verwenden, das Argumente und Schlüsselwortargumente unterstützt. Wie im obigen Beispiel verwenden wir Py_BuildValue(), um das Wörterbuch zu erstellen.
PyObject *dict;
...
dict = Py_BuildValue("{s:i}", "name", val);
result = PyObject_Call(my_callback, NULL, dict);
Py_DECREF(dict);
if (result == NULL)
return NULL; /* Pass error back */
/* Here maybe use the result */
Py_DECREF(result);
1.7. Extrahieren von Parametern in Erweiterungsfunktionen¶
Die Funktion PyArg_ParseTuple() wird wie folgt deklariert
int PyArg_ParseTuple(PyObject *arg, const char *format, ...);
Das Argument arg muss ein Tupel-Objekt sein, das eine Argumentliste enthält, die von Python an eine C-Funktion übergeben wurde. Das Argument format muss eine Formatzeichenkette sein, deren Syntax in Argumente parsen und Werte erstellen im Python/C API-Referenzhandbuch erklärt wird. Die übrigen Argumente müssen Adressen von Variablen sein, deren Typ durch die Formatzeichenkette bestimmt wird.
Beachten Sie, dass PyArg_ParseTuple() prüft, ob die Python-Argumente die erforderlichen Typen haben, aber es kann nicht die Gültigkeit der Adressen von C-Variablen überprüfen, die an den Aufruf übergeben werden: Wenn Sie dort Fehler machen, wird Ihr Code wahrscheinlich abstürzen oder zumindest zufällige Bits im Speicher überschreiben. Seien Sie also vorsichtig!
Beachten Sie, dass alle Python-Objekt-Referenzen, die dem Aufrufer zur Verfügung gestellt werden, **entliehene** Referenzen sind; dekrementieren Sie ihre Referenzanzahl nicht!
Einige Beispielaufrufe
#define PY_SSIZE_T_CLEAN
#include <Python.h>
int ok;
int i, j;
long k, l;
const char *s;
Py_ssize_t size;
ok = PyArg_ParseTuple(args, ""); /* No arguments */
/* Python call: f() */
ok = PyArg_ParseTuple(args, "s", &s); /* A string */
/* Possible Python call: f('whoops!') */
ok = PyArg_ParseTuple(args, "lls", &k, &l, &s); /* Two longs and a string */
/* Possible Python call: f(1, 2, 'three') */
ok = PyArg_ParseTuple(args, "(ii)s#", &i, &j, &s, &size);
/* A pair of ints and a string, whose size is also returned */
/* Possible Python call: f((1, 2), 'three') */
{
const char *file;
const char *mode = "r";
int bufsize = 0;
ok = PyArg_ParseTuple(args, "s|si", &file, &mode, &bufsize);
/* A string, and optionally another string and an integer */
/* Possible Python calls:
f('spam')
f('spam', 'w')
f('spam', 'wb', 100000) */
}
{
int left, top, right, bottom, h, v;
ok = PyArg_ParseTuple(args, "((ii)(ii))(ii)",
&left, &top, &right, &bottom, &h, &v);
/* A rectangle and a point */
/* Possible Python call:
f(((0, 0), (400, 300)), (10, 10)) */
}
{
Py_complex c;
ok = PyArg_ParseTuple(args, "D:myfunction", &c);
/* a complex, also providing a function name for errors */
/* Possible Python call: myfunction(1+2j) */
}
1.8. Schlüsselwortparameter für Erweiterungsfunktionen¶
Die Funktion PyArg_ParseTupleAndKeywords() wird wie folgt deklariert
int PyArg_ParseTupleAndKeywords(PyObject *arg, PyObject *kwdict,
const char *format, char * const *kwlist, ...);
Die Parameter arg und format sind identisch mit denen der Funktion PyArg_ParseTuple(). Der Parameter kwdict ist das Wörterbuch der Schlüsselwörter, das als dritter Parameter vom Python-Runtime empfangen wird. Der Parameter kwlist ist eine mit NULL endende Liste von Zeichenketten, die die Parameter identifizieren; die Namen werden mit den Typinformationen aus format von links nach rechts abgeglichen. Bei Erfolg gibt PyArg_ParseTupleAndKeywords() true zurück, andernfalls gibt sie false zurück und löst eine entsprechende Ausnahme aus.
Hinweis
Verschachtelte Tupel können nicht beim Verwenden von Schlüsselwortargumenten geparst werden! Übergebene Schlüsselwortparameter, die nicht in kwlist vorhanden sind, lösen eine TypeError aus.
Hier ist ein Beispielmodul, das Schlüsselwörter verwendet, basierend auf einem Beispiel von Geoff Philbrick (philbrick@hks.com)
#define PY_SSIZE_T_CLEAN
#include <Python.h>
static PyObject *
keywdarg_parrot(PyObject *self, PyObject *args, PyObject *keywds)
{
int voltage;
const char *state = "a stiff";
const char *action = "voom";
const char *type = "Norwegian Blue";
static char *kwlist[] = {"voltage", "state", "action", "type", NULL};
if (!PyArg_ParseTupleAndKeywords(args, keywds, "i|sss", kwlist,
&voltage, &state, &action, &type))
return NULL;
printf("-- This parrot wouldn't %s if you put %i Volts through it.\n",
action, voltage);
printf("-- Lovely plumage, the %s -- It's %s!\n", type, state);
Py_RETURN_NONE;
}
static PyMethodDef keywdarg_methods[] = {
/* The cast of the function is necessary since PyCFunction values
* only take two PyObject* parameters, and keywdarg_parrot() takes
* three.
*/
{"parrot", (PyCFunction)(void(*)(void))keywdarg_parrot, METH_VARARGS | METH_KEYWORDS,
"Print a lovely skit to standard output."},
{NULL, NULL, 0, NULL} /* sentinel */
};
static struct PyModuleDef keywdarg_module = {
.m_base = PyModuleDef_HEAD_INIT,
.m_name = "keywdarg",
.m_size = 0,
.m_methods = keywdarg_methods,
};
PyMODINIT_FUNC
PyInit_keywdarg(void)
{
return PyModuleDef_Init(&keywdarg_module);
}
1.9. Erstellen beliebiger Werte¶
Diese Funktion ist das Gegenstück zu PyArg_ParseTuple(). Sie wird wie folgt deklariert
PyObject *Py_BuildValue(const char *format, ...);
Sie erkennt eine Reihe von Format-Einheiten, die den von PyArg_ParseTuple() erkannten ähneln, aber die Argumente (die Eingaben für die Funktion, nicht Ausgaben) dürfen keine Zeiger sein, nur Werte. Sie gibt ein neues Python-Objekt zurück, das für die Rückgabe aus einer von Python aufgerufenen C-Funktion geeignet ist.
Ein Unterschied zu PyArg_ParseTuple(): Während letztere verlangt, dass ihr erstes Argument ein Tupel ist (da Python-Argumentlisten intern immer als Tupel dargestellt werden), erstellt Py_BuildValue() nicht immer ein Tupel. Sie erstellt nur dann ein Tupel, wenn ihre Formatzeichenkette zwei oder mehr Format-Einheiten enthält. Wenn die Formatzeichenkette leer ist, gibt sie None zurück; wenn sie genau eine Format-Einheit enthält, gibt sie das Objekt zurück, das von dieser Format-Einheit beschrieben wird. Um sie zu zwingen, ein Tupel der Größe 0 oder 1 zurückzugeben, setzen Sie die Formatzeichenkette in Klammern.
Beispiele (links der Aufruf, rechts der resultierende Python-Wert)
Py_BuildValue("") None
Py_BuildValue("i", 123) 123
Py_BuildValue("iii", 123, 456, 789) (123, 456, 789)
Py_BuildValue("s", "hello") 'hello'
Py_BuildValue("y", "hello") b'hello'
Py_BuildValue("ss", "hello", "world") ('hello', 'world')
Py_BuildValue("s#", "hello", 4) 'hell'
Py_BuildValue("y#", "hello", 4) b'hell'
Py_BuildValue("()") ()
Py_BuildValue("(i)", 123) (123,)
Py_BuildValue("(ii)", 123, 456) (123, 456)
Py_BuildValue("(i,i)", 123, 456) (123, 456)
Py_BuildValue("[i,i]", 123, 456) [123, 456]
Py_BuildValue("{s:i,s:i}",
"abc", 123, "def", 456) {'abc': 123, 'def': 456}
Py_BuildValue("((ii)(ii)) (ii)",
1, 2, 3, 4, 5, 6) (((1, 2), (3, 4)), (5, 6))
1.10. Referenzzähler¶
In Sprachen wie C oder C++ ist der Programmierer für die dynamische Speicherzuweisung und -freigabe im Heap verantwortlich. In C geschieht dies mit den Funktionen malloc() und free(). In C++ werden die Operatoren new und delete mit im Wesentlichen derselben Bedeutung verwendet, und wir beschränken die folgende Diskussion auf den C-Fall.
Jeder mit malloc() zugewiesene Speicherblock sollte schließlich durch genau einen Aufruf von free() an den Pool des verfügbaren Speichers zurückgegeben werden. Es ist wichtig, free() zum richtigen Zeitpunkt aufzurufen. Wenn die Adresse eines Blocks vergessen wird, aber free() dafür nicht aufgerufen wird, kann der von ihm belegte Speicher nicht wiederverwendet werden, bis das Programm beendet ist. Dies nennt man einen Speicherleck. Wenn ein Programm free() für einen Block aufruft und dann weiter mit dem Block arbeitet, entsteht ein Konflikt mit der Wiederverwendung des Blocks durch einen anderen malloc()-Aufruf. Dies nennt man Verwendung von freigegebenem Speicher. Es hat die gleichen schlimmen Folgen wie die Referenzierung von uninitialisierten Daten – Core-Dumps, falsche Ergebnisse, mysteriöse Abstürze.
Häufige Ursachen für Speicherlecks sind ungewöhnliche Pfade durch den Code. Zum Beispiel kann eine Funktion einen Speicherblock zuweisen, eine Berechnung durchführen und dann den Block wieder freigeben. Eine Änderung der Anforderungen an die Funktion kann nun eine Prüfung in die Berechnung einfügen, die einen Fehlerzustand erkennt und die Funktion vorzeitig verlassen kann. Es ist leicht, den zugewiesenen Speicherblock zu vergessen, wenn dieser vorzeitige Ausstieg gewählt wird, insbesondere wenn er später zum Code hinzugefügt wird. Solche Lecks werden, sobald sie eingeführt wurden, oft lange unentdeckt: Der Fehler-Ausstieg wird nur in einem kleinen Bruchteil aller Aufrufe genommen, und die meisten modernen Maschinen haben reichlich virtuellen Speicher, sodass das Leck nur in einem lang laufenden Prozess, der die leckende Funktion häufig verwendet, offensichtlich wird. Daher ist es wichtig, Lecks zu verhindern, indem eine Codierungsvereinbarung oder -strategie vorhanden ist, die diese Art von Fehlern minimiert.
Da Python intensiv von malloc() und free() Gebrauch macht, benötigt es eine Strategie, um sowohl Speicherlecks als auch die Verwendung von freigegebenem Speicher zu vermeiden. Die gewählte Methode wird Referenzzählung genannt. Das Prinzip ist einfach: Jedes Objekt enthält einen Zähler, der erhöht wird, wenn eine Referenz auf das Objekt irgendwo gespeichert wird, und der verringert wird, wenn eine Referenz darauf gelöscht wird. Wenn der Zähler Null erreicht, wurde die letzte Referenz auf das Objekt gelöscht und das Objekt wird freigegeben.
Eine alternative Strategie wird als automatische Speicherbereinigung (Garbage Collection) bezeichnet. (Manchmal wird auch die Referenzzählung als Strategie der Speicherbereinigung bezeichnet, daher meine Verwendung von „automatisch“ zur Unterscheidung der beiden.) Der große Vorteil der automatischen Speicherbereinigung ist, dass der Benutzer free() nicht explizit aufrufen muss. (Ein weiterer angebliche Vorteil ist eine Verbesserung der Geschwindigkeit oder Speichernutzung – dies ist jedoch keine harte Tatsache.) Der Nachteil ist, dass es für C keinen wirklich portablen automatischen Speicherbereiniger gibt, während die Referenzzählung portabel implementiert werden kann (solange die Funktionen malloc() und free() verfügbar sind – was der C-Standard garantiert). Vielleicht wird eines Tages ein ausreichend portabler automatischer Speicherbereiniger für C verfügbar sein. Bis dahin müssen wir mit Referenzzählern leben.
Während Python die traditionelle Implementierung der Referenzzählung verwendet, bietet es auch einen Zyklendetektor, der Referenzzyklen erkennt. Dies ermöglicht es Anwendungen, sich keine Sorgen über die Erstellung direkter oder indirekter zirkulärer Referenzen zu machen; dies sind die Schwächen der Speicherbereinigung, die nur mit Referenzzählung implementiert wird. Referenzzyklen bestehen aus Objekten, die (möglicherweise indirekte) Referenzen auf sich selbst enthalten, sodass jedes Objekt im Zyklus eine Referenzanzahl ungleich Null hat. Typische Referenzzählungs-Implementierungen können den Speicher von Objekten in einem Referenzzyklus oder von Objekten, auf die aus dem Zyklus verwiesen wird, nicht wiederherstellen, auch wenn es keine weiteren Referenzen auf den Zyklus selbst gibt.
Der Zyklendetektor kann Garbage-Zyklen erkennen und sie wiederherstellen. Das Modul gc stellt eine Möglichkeit bereit, den Detektor auszuführen (die Funktion collect()) sowie Konfigurationsschnittstellen und die Möglichkeit, den Detektor zur Laufzeit zu deaktivieren.
1.10.1. Referenzzählung in Python¶
Es gibt zwei Makros, Py_INCREF(x) und Py_DECREF(x), die das Erhöhen und Verringern der Referenzanzahl behandeln. Py_DECREF() gibt das Objekt auch frei, wenn die Zählung Null erreicht. Aus Flexibilitätsgründen ruft es free() nicht direkt auf – vielmehr erfolgt ein Aufruf über einen Funktionszeiger im Typobjekt des Objekts. Zu diesem Zweck (und anderen) enthält jedes Objekt auch einen Zeiger auf sein Typobjekt.
Die große Frage bleibt nun: Wann verwendet man Py_INCREF(x) und Py_DECREF(x)? Lassen Sie uns zunächst einige Begriffe einführen. Niemand „besitzt“ ein Objekt; jedoch können Sie eine Referenz besitzen. Die Referenzanzahl eines Objekts wird nun als die Anzahl der besessenen Referenzen darauf definiert. Der Besitzer einer Referenz ist dafür verantwortlich, Py_DECREF() aufzurufen, wenn die Referenz nicht mehr benötigt wird. Der Besitz einer Referenz kann übertragen werden. Es gibt drei Möglichkeiten, eine besessene Referenz zu entsorgen: weitergeben, speichern oder Py_DECREF() aufrufen. Das Vergessen, eine besessene Referenz zu entsorgen, führt zu einem Speicherleck.
Es ist auch möglich, eine Referenz auf ein Objekt zu leihen [2]. Der Leihende einer Referenz sollte Py_DECREF() nicht aufrufen. Der Leihende darf das Objekt nicht länger behalten, als der Eigentümer, von dem es geliehen wurde. Die Verwendung einer geliehenen Referenz, nachdem der Eigentümer sie entsorgt hat, birgt das Risiko, freigegebenen Speicher zu verwenden und sollte vollständig vermieden werden [3].
Der Vorteil des Leihens gegenüber dem Besitzen einer Referenz besteht darin, dass Sie sich nicht um die Entsorgung der Referenz auf allen möglichen Pfaden durch den Code kümmern müssen – mit einer geliehenen Referenz laufen Sie also nicht Gefahr, ein Leck zu verursachen, wenn ein vorzeitiger Ausstieg erfolgt. Der Nachteil des Leihens gegenüber dem Besitzen besteht darin, dass es einige knifflige Situationen gibt, in denen eine geliehene Referenz in scheinbar korrektem Code verwendet werden kann, nachdem der Eigentümer, von dem sie geliehen wurde, sie tatsächlich entsorgt hat.
Eine geliehene Referenz kann durch Aufrufen von Py_INCREF() in eine besessene Referenz umgewandelt werden. Dies beeinflusst nicht den Status des Eigentümers, von dem die Referenz geliehen wurde – es wird eine neue besessene Referenz erstellt und vollständige Eigentümerverantwortlichkeiten übertragen (der neue Eigentümer muss die Referenz ordnungsgemäß entsorgen, ebenso wie der vorherige Eigentümer).
1.10.2. Eigentumsregeln¶
Immer wenn eine Objekt-Referenz in oder aus einer Funktion übergeben wird, gehört es zur Spezifikation der Schnittstelle der Funktion, ob das Eigentum mit der Referenz übertragen wird oder nicht.
Die meisten Funktionen, die eine Referenz auf ein Objekt zurückgeben, übertragen das Eigentum mit der Referenz. Insbesondere alle Funktionen, deren Aufgabe es ist, ein neues Objekt zu erstellen, wie z. B. PyLong_FromLong() und Py_BuildValue(), übertragen das Eigentum an den Empfänger. Selbst wenn das Objekt nicht tatsächlich neu ist, erhalten Sie immer noch das Eigentum an einer neuen Referenz auf dieses Objekt. Zum Beispiel unterhält PyLong_FromLong() einen Cache für beliebte Werte und kann eine Referenz auf ein gecachtes Element zurückgeben.
Viele Funktionen, die Objekte aus anderen Objekten extrahieren, übertragen ebenfalls das Eigentum mit der Referenz, zum Beispiel PyObject_GetAttrString(). Das Bild ist hier jedoch weniger klar, da einige gängige Routinen Ausnahmen darstellen: PyTuple_GetItem(), PyList_GetItem(), PyDict_GetItem() und PyDict_GetItemString() geben alle Referenzen zurück, die Sie von dem Tupel, der Liste oder dem Wörterbuch leihen.
Die Funktion PyImport_AddModule() gibt ebenfalls eine geliehene Referenz zurück, auch wenn sie tatsächlich das zurückgegebene Objekt erstellt: Dies ist möglich, da eine besessene Referenz auf das Objekt in sys.modules gespeichert ist.
Wenn Sie eine Objekt-Referenz in eine andere Funktion übergeben, leiht sich die Funktion im Allgemeinen die Referenz von Ihnen – wenn sie sie speichern muss, verwendet sie Py_INCREF(), um ein unabhängiger Eigentümer zu werden. Es gibt genau zwei wichtige Ausnahmen von dieser Regel: PyTuple_SetItem() und PyList_SetItem(). Diese Funktionen übernehmen das Eigentum an dem ihnen übergebenen Element – auch wenn sie fehlschlagen! (Beachten Sie, dass PyDict_SetItem() und ähnliche Funktionen das Eigentum nicht übernehmen – sie sind „normal“.)
Wenn eine C-Funktion von Python aufgerufen wird, leiht sie sich Referenzen auf ihre Argumente vom Aufrufer. Der Aufrufer besitzt eine Referenz auf das Objekt, sodass die Lebensdauer der geliehenen Referenz bis zur Rückgabe der Funktion garantiert ist. Nur wenn eine solche geliehene Referenz gespeichert oder weitergegeben werden muss, muss sie durch Aufrufen von Py_INCREF() in eine besessene Referenz umgewandelt werden.
Die von einer von Python aufgerufenen C-Funktion zurückgegebene Objekt-Referenz muss eine besessene Referenz sein – das Eigentum wird von der Funktion auf ihren Aufrufer übertragen.
1.10.3. Dünnes Eis¶
Es gibt einige Situationen, in denen eine scheinbar harmlose Verwendung einer geliehenen Referenz zu Problemen führen kann. Diese haben alle mit impliziten Aufrufen des Interpreters zu tun, die dazu führen können, dass der Eigentümer einer Referenz diese entsorgt.
Der erste und wichtigste Fall, den man kennen muss, ist die Verwendung von Py_DECREF() auf einem nicht verbundenen Objekt während der Ausleihe einer Referenz auf ein Listen-Element. Zum Beispiel
void
bug(PyObject *list)
{
PyObject *item = PyList_GetItem(list, 0);
PyList_SetItem(list, 1, PyLong_FromLong(0L));
PyObject_Print(item, stdout, 0); /* BUG! */
}
Diese Funktion leiht sich zuerst eine Referenz auf list[0], ersetzt dann list[1] durch den Wert 0 und gibt schließlich die geliehene Referenz aus. Sieht harmlos aus, oder? Aber das ist es nicht!
Verfolgen wir den Kontrollfluss in PyList_SetItem(). Die Liste besitzt Referenzen auf alle ihre Elemente, also muss sie beim Ersetzen von Element 1 das ursprüngliche Element 1 entsorgen. Nehmen wir nun an, das ursprüngliche Element 1 war eine Instanz einer benutzerdefinierten Klasse, und nehmen wir weiter an, die Klasse definierte eine __del__()-Methode. Wenn diese Klasseninstanz eine Referenzanzahl von 1 hat, ruft die Entsorgung ihre __del__()-Methode auf. Intern ruft PyList_SetItem() Py_DECREF() für das ersetzte Element auf, was die entsprechende tp_dealloc-Funktion des ersetzten Elements aufruft. Während der Deallokation ruft tp_dealloc tp_finalize auf, das für Klasseninstanzen auf die __del__()-Methode abgebildet wird (siehe PEP 442). Diese gesamte Sequenz geschieht synchron innerhalb des Aufrufs von PyList_SetItem().
Da die __del__()-Methode in Python geschrieben ist, kann sie beliebigen Python-Code ausführen. Könnte sie vielleicht etwas tun, um die Referenz auf item in bug() ungültig zu machen? Das können Sie wetten! Unter der Annahme, dass die Liste, die in bug() übergeben wurde, für die __del__()-Methode zugänglich ist, könnte sie eine Anweisung ausführen, die dem Folgenden entspricht: del list[0], und unter der Annahme, dass dies die letzte Referenz auf dieses Objekt war, würde sie den zugehörigen Speicher freigeben und damit item ungültig machen.
Die Lösung ist, sobald man die Quelle des Problems kennt, einfach: die Referenzanzahl vorübergehend erhöhen. Die korrekte Version der Funktion lautet
void
no_bug(PyObject *list)
{
PyObject *item = PyList_GetItem(list, 0);
Py_INCREF(item);
PyList_SetItem(list, 1, PyLong_FromLong(0L));
PyObject_Print(item, stdout, 0);
Py_DECREF(item);
}
Dies ist eine wahre Geschichte. Eine ältere Version von Python enthielt Varianten dieses Fehlers, und jemand verbrachte eine beträchtliche Zeit in einem C-Debugger, um herauszufinden, warum seine __del__()-Methoden fehlschlagen würden...
Der zweite Fall von Problemen mit einer geliehenen Referenz ist eine Variante, die Threads betrifft. Normalerweise können sich mehrere Threads im Python-Interpreter nicht gegenseitig in die Quere kommen, da es eine globale Sperre gibt, die den gesamten Objektraum von Python schützt. Es ist jedoch möglich, diese Sperre vorübergehend mit dem Makro Py_BEGIN_ALLOW_THREADS freizugeben und mit Py_END_ALLOW_THREADS wieder zu erwerben. Dies ist üblich in der Nähe von blockierenden E/A-Aufrufen, damit andere Threads den Prozessor nutzen können, während sie auf den Abschluss der E/A warten. Offensichtlich hat die folgende Funktion dasselbe Problem wie die vorherige
void
bug(PyObject *list)
{
PyObject *item = PyList_GetItem(list, 0);
Py_BEGIN_ALLOW_THREADS
...some blocking I/O call...
Py_END_ALLOW_THREADS
PyObject_Print(item, stdout, 0); /* BUG! */
}
1.10.4. NULL-Zeiger¶
Im Allgemeinen erwarten Funktionen, die Objekt-Referenzen als Argumente entgegennehmen, nicht, dass Sie ihnen NULL-Zeiger übergeben, und sie werden abstürzen (oder spätere Abstürze verursachen), wenn Sie dies tun. Funktionen, die Objekt-Referenzen zurückgeben, geben im Allgemeinen nur NULL zurück, um anzuzeigen, dass eine Ausnahme aufgetreten ist. Der Grund für die Nichtprüfung auf NULL-Argumente ist, dass Funktionen oft die Objekte, die sie erhalten, an andere Funktionen weitergeben – wenn jede Funktion auf NULL prüfen würde, gäbe es viele redundante Prüfungen und der Code würde langsamer laufen.
Es ist besser, nur am „Ursprung“ auf NULL zu prüfen: wenn ein Zeiger empfangen wird, der NULL sein kann, zum Beispiel von malloc() oder von einer Funktion, die eine Ausnahme auslösen kann.
Die Makros Py_INCREF() und Py_DECREF() prüfen nicht auf NULL-Zeiger – ihre Varianten Py_XINCREF() und Py_XDECREF() jedoch.
Die Makros zur Überprüfung eines bestimmten Objekttyps (Pytype_Check()) überprüfen keine NULL-Zeiger — es gibt viel Code, der mehrere davon hintereinander aufruft, um ein Objekt gegen verschiedene erwartete Typen zu testen, und dies würde redundante Tests erzeugen. Es gibt keine Varianten mit NULL-Überprüfung.
Der C-Funktionsaufrufmechanismus garantiert, dass die an C-Funktionen übergebene Argumentenliste (args in den Beispielen) niemals NULL ist — tatsächlich garantiert er, dass es sich immer um ein Tupel handelt [4].
Es ist ein schwerwiegender Fehler, einen NULL-Zeiger jemals an den Python-Benutzer "entkommen" zu lassen.
1.11. Erweiterungen in C++ schreiben¶
Es ist möglich, Erweiterungsmodule in C++ zu schreiben. Einige Einschränkungen gelten. Wenn das Hauptprogramm (der Python-Interpreter) vom C-Compiler kompiliert und verlinkt wird, können globale oder statische Objekte mit Konstruktoren nicht verwendet werden. Dies ist kein Problem, wenn das Hauptprogramm vom C++-Compiler verlinkt wird. Funktionen, die vom Python-Interpreter aufgerufen werden (insbesondere Modulinitialisierungsfunktionen), müssen mit extern "C" deklariert werden. Es ist unnötig, die Python-Header-Dateien in extern "C" {...} einzuschließen — sie verwenden diese Form bereits, wenn das Symbol __cplusplus definiert ist (alle neueren C++-Compiler definieren dieses Symbol).
1.12. Bereitstellen einer C-API für ein Erweiterungsmodul¶
Viele Erweiterungsmodule stellen nur neue Funktionen und Typen bereit, die von Python aus verwendet werden können, aber manchmal kann der Code in einem Erweiterungsmodul für andere Erweiterungsmodule nützlich sein. Zum Beispiel könnte ein Erweiterungsmodul einen Typ "Collection" implementieren, der wie Listen ohne Ordnung funktioniert. So wie der Standard-Python-List-Typ eine C-API hat, die es Erweiterungsmodulen ermöglicht, Listen zu erstellen und zu manipulieren, sollte dieser neue Collection-Typ eine Reihe von C-Funktionen für die direkte Manipulation von anderen Erweiterungsmodulen haben.
Auf den ersten Blick scheint das einfach zu sein: Schreiben Sie einfach die Funktionen (natürlich ohne sie als static zu deklarieren), stellen Sie eine geeignete Header-Datei bereit und dokumentieren Sie die C-API. Und tatsächlich würde dies funktionieren, wenn alle Erweiterungsmodule immer statisch mit dem Python-Interpreter verlinkt würden. Wenn Module jedoch als gemeinsam genutzte Bibliotheken verwendet werden, sind die in einem Modul definierten Symbole möglicherweise nicht für ein anderes Modul sichtbar. Die Details der Sichtbarkeit hängen vom Betriebssystem ab; einige Systeme verwenden einen globalen Namespace für den Python-Interpreter und alle Erweiterungsmodule (z. B. Windows), während andere eine explizite Liste der importierten Symbole zur Modul-Linkzeit erfordern (AIX ist ein Beispiel) oder eine Auswahl verschiedener Strategien anbieten (die meisten Unices). Und selbst wenn Symbole global sichtbar sind, wurde das Modul, dessen Funktionen man aufrufen möchte, möglicherweise noch nicht geladen!
Portabilität erfordert daher, keine Annahmen über die Symbol-Sichtbarkeit zu treffen. Das bedeutet, dass alle Symbole in Erweiterungsmodulen als static deklariert werden sollten, mit Ausnahme der Initialisierungsfunktion des Moduls, um Namenskonflikte mit anderen Erweiterungsmodulen zu vermeiden (wie in Abschnitt Die Methodentabelle und Initialisierungsfunktion des Moduls diskutiert). Und es bedeutet, dass Symbole, die von anderen Erweiterungsmodulen zugänglich sein *sollten*, auf andere Weise exportiert werden müssen.
Python bietet einen speziellen Mechanismus, um C-Level-Informationen (Zeiger) von einem Erweiterungsmodul an ein anderes zu übergeben: Capsules. Eine Capsule ist ein Python-Datentyp, der einen Zeiger (void*) speichert. Capsules können nur über ihre C-API erstellt und zugegriffen werden, aber sie können wie jedes andere Python-Objekt weitergegeben werden. Insbesondere können sie einem Namen im Namensraum eines Erweiterungsmoduls zugewiesen werden. Andere Erweiterungsmodule können dann dieses Modul importieren, den Wert dieses Namens abrufen und dann den Zeiger aus der Capsule abrufen.
Es gibt viele Möglichkeiten, wie Capsules verwendet werden können, um die C-API eines Erweiterungsmoduls zu exportieren. Jede Funktion könnte ihre eigene Capsule erhalten, oder alle C-API-Zeiger könnten in einem Array gespeichert werden, dessen Adresse in einer Capsule veröffentlicht wird. Und die verschiedenen Aufgaben des Speicherns und Abrufens der Zeiger können auf unterschiedliche Weise zwischen dem Modul, das den Code bereitstellt, und den Client-Modulen verteilt werden.
Welche Methode Sie auch wählen, es ist wichtig, Ihre Capsules richtig zu benennen. Die Funktion PyCapsule_New() nimmt einen Namensparameter (const char*); Sie dürfen einen NULL-Namen übergeben, aber wir empfehlen dringend, einen Namen anzugeben. Richtig benannte Capsules bieten ein gewisses Maß an Laufzeit-Typsicherheit; es gibt keine praktikable Möglichkeit, eine unbenannte Capsule von einer anderen zu unterscheiden.
Insbesondere sollten Capsules, die zur Offenlegung von C-APIs verwendet werden, einen Namen nach dieser Konvention erhalten
modulename.attributename
Die Hilfsfunktion PyCapsule_Import() erleichtert das Laden einer C-API, die über eine Capsule bereitgestellt wird, aber nur, wenn der Name der Capsule dieser Konvention entspricht. Dieses Verhalten gibt C-API-Benutzern eine hohe Sicherheit, dass die geladene Capsule die korrekte C-API enthält.
Das folgende Beispiel demonstriert einen Ansatz, der den größten Teil der Arbeit dem Autor des exportierenden Moduls auferlegt, was für häufig verwendete Bibliotheksmodule angemessen ist. Es speichert alle C-API-Zeiger (im Beispiel nur einen!) in einem Array von void-Zeigern, das zum Wert einer Capsule wird. Die Header-Datei, die dem Modul entspricht, stellt ein Makro bereit, das für den Import des Moduls und den Abruf seiner C-API-Zeiger zuständig ist; Client-Module müssen nur dieses Makro aufrufen, bevor sie auf die C-API zugreifen.
Das exportierende Modul ist eine Modifikation des spam-Moduls aus Abschnitt Ein einfaches Beispiel. Die Funktion spam.system() ruft nicht die C-Bibliotheksfunktion system() direkt auf, sondern eine Funktion PySpam_System(), die in Wirklichkeit natürlich etwas Komplizierteres tun würde (wie z. B. jedem Befehl "spam" hinzuzufügen). Diese Funktion PySpam_System() wird auch an andere Erweiterungsmodule exportiert.
Die Funktion PySpam_System() ist eine normale C-Funktion, die wie alles andere als static deklariert ist
static int
PySpam_System(const char *command)
{
return system(command);
}
Die Funktion spam_system() ist auf triviale Weise modifiziert
static PyObject *
spam_system(PyObject *self, PyObject *args)
{
const char *command;
int sts;
if (!PyArg_ParseTuple(args, "s", &command))
return NULL;
sts = PySpam_System(command);
return PyLong_FromLong(sts);
}
Am Anfang des Moduls, direkt nach der Zeile
#include <Python.h>
müssen zwei weitere Zeilen hinzugefügt werden
#define SPAM_MODULE
#include "spammodule.h"
Das #define wird verwendet, um die Header-Datei anzuweisen, dass sie im exportierenden Modul und nicht in einem Client-Modul eingeschlossen wird. Schließlich muss die mod_exec-Funktion des Moduls die Initialisierung des C-API-Zeigerarrays übernehmen
static int
spam_module_exec(PyObject *m)
{
static void *PySpam_API[PySpam_API_pointers];
PyObject *c_api_object;
/* Initialize the C API pointer array */
PySpam_API[PySpam_System_NUM] = (void *)PySpam_System;
/* Create a Capsule containing the API pointer array's address */
c_api_object = PyCapsule_New((void *)PySpam_API, "spam._C_API", NULL);
if (PyModule_Add(m, "_C_API", c_api_object) < 0) {
return -1;
}
return 0;
}
Beachten Sie, dass PySpam_API als static deklariert ist; andernfalls würde das Zeigerarray verschwinden, wenn PyInit_spam() beendet wird!
Der Großteil der Arbeit steckt in der Header-Datei spammodule.h, die wie folgt aussieht
#ifndef Py_SPAMMODULE_H
#define Py_SPAMMODULE_H
#ifdef __cplusplus
extern "C" {
#endif
/* Header file for spammodule */
/* C API functions */
#define PySpam_System_NUM 0
#define PySpam_System_RETURN int
#define PySpam_System_PROTO (const char *command)
/* Total number of C API pointers */
#define PySpam_API_pointers 1
#ifdef SPAM_MODULE
/* This section is used when compiling spammodule.c */
static PySpam_System_RETURN PySpam_System PySpam_System_PROTO;
#else
/* This section is used in modules that use spammodule's API */
static void **PySpam_API;
#define PySpam_System \
(*(PySpam_System_RETURN (*)PySpam_System_PROTO) PySpam_API[PySpam_System_NUM])
/* Return -1 on error, 0 on success.
* PyCapsule_Import will set an exception if there's an error.
*/
static int
import_spam(void)
{
PySpam_API = (void **)PyCapsule_Import("spam._C_API", 0);
return (PySpam_API != NULL) ? 0 : -1;
}
#endif
#ifdef __cplusplus
}
#endif
#endif /* !defined(Py_SPAMMODULE_H) */
Alles, was ein Client-Modul tun muss, um Zugriff auf die Funktion PySpam_System() zu haben, ist, die Funktion (oder besser gesagt das Makro) import_spam() in seiner mod_exec-Funktion aufzurufen
static int
client_module_exec(PyObject *m)
{
if (import_spam() < 0) {
return -1;
}
/* additional initialization can happen here */
return 0;
}
Der Hauptnachteil dieses Ansatzes ist, dass die Datei spammodule.h recht kompliziert ist. Die Grundstruktur ist jedoch für jede exportierte Funktion gleich, so dass sie nur einmal gelernt werden muss.
Schließlich sei erwähnt, dass Capsules zusätzliche Funktionalität bieten, die besonders nützlich für die Speicherzuweisung und -freigabe des in einer Capsule gespeicherten Zeigers ist. Die Details sind im Python/C-API-Referenzhandbuch im Abschnitt Capsules und in der Implementierung von Capsules (Dateien Include/pycapsule.h und Objects/pycapsule.c im Python-Quellcode-Verteiler) beschrieben.
Fußnoten