Python Extension Programming mit C: Schnelle, native Module erstellen
Einführung
Jeder Code, den Sie in einer kompilierten Sprache wie C, C++ oder Java schreiben, kann in ein anderes Python-Skript integriert oder importiert werden. Dieser Code wird als Extension bezeichnet.
Voraussetzungen für Extensions
Für den Start benötigen Sie die Python-Header-Dateien:
- Unix: Meist Installation eines Entwicklerpakets wie
python2.5-dev(bzw. entsprechende Version). - Windows: Header sind Teil des binären Python-Installers.
Zusätzlich wird gute C/C++-Kenntnis vorausgesetzt.
Erster Blick auf eine Python-Extension
Gruppieren Sie Ihren Code in vier Teile:
- Header-Datei
Python.h. - C-Funktionen, die Sie als Interface vom Modul exponieren wollen.
- Tabelle, die Funktionsnamen (wie Python-Entwickler sie sehen) auf C-Funktionen im Modul abbildet.
- Initialisierungsfunktion.
Die Header-Datei Python.h
Inkludieren Sie Python.h in Ihrer C-Quelldatei für Zugriff auf die interne Python-API zum Einbinden des Moduls in den Interpreter.
Inkludieren Sie Python.h vor anderen Headern. Folgen Sie den Includes mit den Funktionen, die Sie aus Python aufrufen wollen.
Die C-Funktionen
Signaturen der C-Implementierung Ihrer Funktionen haben immer eine von drei Formen:
static PyObject *MyFunction(PyObject *self, PyObject *args); static PyObject *MyFunctionWithKeywords(PyObject *self, PyObject *args, PyObject *kw); static PyObject *MyFunctionWithNoArgs(PyObject *self);
Jede Deklaration gibt ein Python-Objekt zurück. Es gibt keine void-Funktionen wie in C. Wenn Sie keinen Wert zurückgeben wollen, geben Sie das C-Äquivalent von Pythons None zurück. Die Python-Header definieren das Makro Py_RETURN_NONE dafür.
Namen Ihrer C-Funktionen sind frei wählbar (sie sind außerhalb des Moduls nicht sichtbar). Sie sind als static definiert.
Üblicherweise kombinieren Sie Modul- und Funktionsnamen:
static PyObject *module_func(PyObject *self, PyObject *args) {
/* Your stuff here */
Py_RETURN_NONE;
}
Dies ist eine Python-Funktion func im Modul module. Zeiger auf Ihre C-Funktionen kommen in die Methodentabelle des Moduls.
Die Method Mapping Table
Ein einfaches Array von PyMethodDef-Strukturen:
struct PyMethodDef {
char *ml_name;
PyCFunction ml_meth;
int ml_flags;
char *ml_doc;
};
- ml_name – Funktionsname wie vom Python-Interpreter präsentiert.
- ml_meth – Adresse einer Funktion mit einer der obigen Signaturen.
- ml_flags – Welche der drei Signaturen
ml_methverwendet. MeistMETH_VARARGS. Kann mitMETH_KEYWORDSper bitweisem OR kombiniert werden für Keyword-Argumente.METH_NOARGSfür keine Argumente. - ml_doc – Docstring für die Funktion (kann
NULLsein).
Die Tabelle muss mit einem Sentinel (NULL, NULL, 0, NULL) terminiert werden.
Beispiel
static PyMethodDef module_methods[] = {
{"func", (PyCFunction)module_func, METH_NOARGS, NULL},
{NULL, NULL, 0, NULL}
};
Die Initialisierungsfunktion
Der letzte Teil des Extensionsmoduls. Wird vom Python-Interpreter beim Laden aufgerufen. Muss initModule heißen, wobei Module der Modulname ist.
Die Funktion muss aus der Bibliothek exportiert werden. Python-Header definieren PyMODINIT_FUNC für die passenden Incantations. Einfach beim Definieren verwenden.
Struktur:
PyMODINIT_FUNC initModule() {
Py_InitModule3("func", module_methods, "docstring...");
}
func– Zu exportierende Funktion.module_methods– Name der Mapping-Tabelle.docstring– Kommentar für die Extension.
Komplettes Beispiel
#includestatic PyObject *module_func(PyObject *self, PyObject *args) { /* Do your stuff here */ Py_RETURN_NONE; } static PyMethodDef module_methods[] = { {"func", (PyCFunction)module_func, METH_NOARGS, NULL}, {NULL, NULL, 0, NULL} }; PyMODINIT_FUNC initModule() { Py_InitModule3("func", module_methods, "docstring..."); }
Einfaches Beispiel: Hello World
#includestatic PyObject* helloworld(PyObject* self) { return Py_BuildValue("s", "Hello, Python extensions!!"); } static char helloworld_docs[] = "helloworld(): Any message you want to put here!!\n"; static PyMethodDef helloworld_funcs[] = { {"helloworld", (PyCFunction)helloworld, METH_NOARGS, helloworld_docs}, {NULL} }; void inithelloworld(void) { Py_InitModule3("helloworld", helloworld_funcs, "Extension module example!"); }
Py_BuildValue baut einen Python-Wert. Speichern als hello.c. Kompilieren und installieren folgt.
Extensions bauen und installieren
Das distutils-Paket vereinfacht Distribution von Python-Modulen (reines Python und Extensions) standardisiert. Module werden in Quellform verteilt und via Setup-Skript (setup.py) gebaut und installiert.
Für obiges Modul:
from distutils.core import setup, Extension
setup(name='helloworld', version='1.0',
ext_modules=[Extension('helloworld', ['hello.c'])])
Kommando für Kompilierung, Linking und Installation:
$ python setup.py install
Auf Unix-Systemen meist als root nötig (Schreibrechte für site-packages). Unter Windows meist unproblematisch.
Extensions importieren
Nach Installation können Sie die Extension in Python importieren und aufrufen:
#!/usr/bin/python import helloworld print(helloworld.helloworld())
Ausgabe:
Hello, Python extensions!!
Funktionsparameter übergeben
Für Funktionen mit Argumenten nutzen Sie die anderen Signaturen. Beispiel für Funktion mit Parametern:
static PyObject *module_func(PyObject *self, PyObject *args) {
/* Parse args and do something interesting */
Py_RETURN_NONE;
}
Methodentabelle mit neuem Eintrag:
static PyMethodDef module_methods[] = {
{"func", (PyCFunction)module_func, METH_NOARGS, NULL},
{"func", module_func, METH_VARARGS, NULL},
{NULL, NULL, 0, NULL}
};
Nutzen Sie PyArg_ParseTuple zum Extrahieren der Argumente aus dem PyObject-Pointer.
static PyObject *module_func(PyObject *self, PyObject *args) {
int i; double d; char *s;
if (!PyArg_ParseTuple(args, "ids", &i, &d, &s)) {
return NULL;
}
/* Do something interesting */
Py_RETURN_NONE;
}
Nach Neukompilierung und Import können Sie die Funktion mit verschiedenen Argumenten aufrufen:
module.func(1, s="three", d=2.0) module.func(i=1, d=2.0, s="three") module.func(s="three", d=2.0, i=1)
Die PyArg_ParseTuple-Funktion
int PyArg_ParseTuple(PyObject* tuple, char* format, ...)
Gibt 0 bei Fehler, !=0 bei Erfolg zurück. tuple ist der zweite Argument der C-Funktion. format ist ein C-String, der Pflicht- und Optionale Argumente beschreibt.
Format-Codes
| Code | C-Typ | Bedeutung |
|---|---|---|
| c | char | Python-String Länge 1 → C char |
| d | double | Python float → C double |
| f | float | Python float → C float |
| i | int | Python int → C int |
| l | long | Python int → C long |
| L | long long | Python int → C long long |
| O | PyObject* | Non-NULL borrowed reference |
| s | char* | Python-String ohne embedded nulls → C char* |
| s# | char*+int | Beliebiger Python-String → C Adresse und Länge |
| t# | char*+int | Read-only single-segment buffer → C Adresse und Länge |
| u | Py_UNICODE* | Python Unicode ohne embedded nulls → C |
| u# | Py_UNICODE*+int | Beliebiges Python Unicode → C Adresse und Länge |
| w# | char*+int | Read/write single-segment buffer → C Adresse und Länge |
| z | char* | Wie s, akzeptiert auch None (setzt C char* auf NULL) |
| z# | char*+int | Wie s#, akzeptiert auch None |
| (...) | as per ... | Python-Sequenz → ein Argument pro Item |
| | | Folgende Argumente sind optional | |
| : | Formatende, Funktionsname für Fehlermeldungen | |
| ; | Formatende, kompletter Fehlermeldungstext |
Werte zurückgeben
Py_BuildValue nutzt einen Formatstring wie PyArg_ParseTuple. Statt Adressen übergeben Sie die eigentlichen Werte. Beispiel für add-Funktion:
static PyObject *foo_add(PyObject *self, PyObject *args) {
int a, b;
if (!PyArg_ParseTuple(args, "ii", &a, &b)) {
return NULL;
}
return Py_BuildValue("i", a + b);
}
Python-Äquivalent:
def add(a, b):
return (a + b)
Zwei Werte zurückgeben (werden in Python als Liste/Tupel erfasst):
static PyObject *foo_add_subtract(PyObject *self, PyObject *args) {
int a, b;
if (!PyArg_ParseTuple(args, "ii", &a, &b)) {
return NULL;
}
return Py_BuildValue("ii", a + b, a - b);
}
Python-Äquivalent:
def add_subtract(a, b):
return (a + b, a - b)
Die Py_BuildValue-Funktion
PyObject* Py_BuildValue(char* format, ...)
format beschreibt das zu erstellende Python-Objekt. Die folgenden Argumente sind C-Werte. Das Ergebnis ist eine neue Referenz.
Häufige Format-Codes
| Code | C-Typ | Bedeutung |
|---|---|---|
| c | char | C char → Python-String Länge 1 |
| d | double | C double → Python float |
| f | float | C float → Python float |
| i | int | C int → Python int |
| l | long | C long → Python int |
| N | PyObject* | Python-Objekt übergeben, Referenz stehlen |
| O | PyObject* | Python-Objekt übergeben, INCREF wie normal |
| O& | convert+void* | Beliebige Konvertierung |
| s | char* | C 0-terminierter char* → Python-String, NULL → None |
| s# | char*+int | C char* und Länge → Python-String, NULL → None |
| u | Py_UNICODE* | C-breiter, null-terminierter String → Python Unicode, NULL → None |
| u# | Py_UNICODE*+int | C-breiter String und Länge → Python Unicode, NULL → None |
| w# | char*+int | Read/write single-segment buffer → C Adresse und Länge |
| z | char* | Wie s, akzeptiert None (setzt C char* auf NULL) |
| z# | char*+int | Wie s#, akzeptiert None |
| (...) | as per ... | Erzeugt Python-Tupel aus C-Werten |
| [...] | as per ... | Erzeugt Python-Liste aus C-Werten |
| {...} | as per ... | Erzeugt Python-Dict aus C-Werten, abwechselnd Keys und Values |
Code {...} baut Dictionaries aus gerader Anzahl C-Werte, abwechselnd Keys und Values. Beispiel: Py_BuildValue("{issi}",23,"zig","zag",42) ergibt Python-Dict {23:'zig','zag':42}.
Python
- Python Objektorientierte Programmierung meistern: Klassen, Vererbung, Kapselung & Polymorphie
- Python print()-Funktion: Praktischer Leitfaden mit Beispielen
- Pythons strip()-Methode meistern: Umfassender Leitfaden & Praxisbeispiele
- Pythons str.count(): Zeichen & Teilstrings zählen mit Beispielen
- Python format()-Strings meistern mit klaren Beispielen
- Pythons String.find()-Methode meistern: Syntax, Beispiele & Alternativen
- Python Lambda-Funktionen meistern: Praxisbeispiele & Best Practices
- Python round()-Funktion erklärt mit praktischen Beispielen
- Pythons map()-Funktion meistern: Syntax, Beispiele und Best Practices
- Python timeit(): Ausführungszeit messen mit praktischen Beispielen