Python - Erweiterungsprogrammierung mit C
Vorherige SeiteNächste Seite
Jeder Code, den Sie in einer beliebigen kompilierten Sprache wie C, C++ oder Java schreiben, kann in ein anderes Python-Skript integriert oder importiert werden. Dieser Code wird als "Erweiterung" betrachtet.
Ein Python-Erweiterungsmodul ist nichts anderes als eine normale C-Bibliothek. Auf Unix-Rechnern enden diese Bibliotheken normalerweise auf .so (für gemeinsames Objekt). Auf Windows-Computern sehen Sie normalerweise .dll (für dynamisch verknüpfte Bibliotheken).
Voraussetzungen für das Schreiben von Erweiterungen
Um mit dem Schreiben Ihrer Erweiterung zu beginnen, benötigen Sie die Python-Header-Dateien.
-
Auf Unix-Rechnern erfordert dies normalerweise die Installation eines entwicklerspezifischen Pakets wie python2.5-dev.
-
Windows-Benutzer erhalten diese Header als Teil des Pakets, wenn sie das binäre Python-Installationsprogramm verwenden.
Darüber hinaus wird davon ausgegangen, dass Sie über gute Kenntnisse in C oder C++ verfügen, um Python-Erweiterungen mithilfe der C-Programmierung zu schreiben.
Sehen Sie sich zuerst eine Python-Erweiterung an
Für Ihren ersten Blick auf ein Python-Erweiterungsmodul müssen Sie Ihren Code in vier Teile gruppieren −
-
Die Header-Datei Python.h .
-
Die C-Funktionen, die Sie als Schnittstelle Ihres Moduls bereitstellen möchten.
-
Eine Tabelle, die die Namen Ihrer Funktionen, wie sie Python-Entwickler sehen, C-Funktionen innerhalb des Erweiterungsmoduls zuordnet.
-
Eine Initialisierungsfunktion.
Die Header-Datei Python.h
Sie müssen Python.h einschließen Header-Datei in Ihrer C-Quelldatei, die Ihnen Zugriff auf die interne Python-API gibt, die verwendet wird, um Ihr Modul in den Interpreter einzubinden.
Stellen Sie sicher, dass Sie Python.h vor allen anderen Headern einfügen, die Sie möglicherweise benötigen. Sie müssen den Includes mit den Funktionen folgen, die Sie von Python aufrufen möchten.
Die C-Funktionen
Die Signaturen der C-Implementierung Ihrer Funktionen nehmen immer eine der folgenden drei Formen an −
static PyObject *MyFunction( PyObject *self, PyObject *args ); static PyObject *MyFunctionWithKeywords(PyObject *self, PyObject *args, PyObject *kw); static PyObject *MyFunctionWithNoArgs( PyObject *self );
Jede der vorhergehenden Deklarationen gibt ein Python-Objekt zurück. Es gibt keine Leere Funktion in Python genauso wie in C. Wenn Sie nicht möchten, dass Ihre Funktionen einen Wert zurückgeben, geben Sie das C-Äquivalent von Pythons None zurück Wert. Die Python-Header definieren ein Makro, Py_RETURN_NONE, das dies für uns erledigt.
Die Namen Ihrer C-Funktionen können beliebig sein, da sie außerhalb des Erweiterungsmoduls nie zu sehen sind. Sie sind als statisch definiert Funktion.
Ihre C-Funktionen werden normalerweise benannt, indem die Python-Modul- und Funktionsnamen miteinander kombiniert werden, wie hier gezeigt −
static PyObject *module_func(PyObject *self, PyObject *args) { /* Do your stuff here. */ Py_RETURN_NONE; }
Dies ist eine Python-Funktion namens func innerhalb des Moduls module . Sie werden Zeiger auf Ihre C-Funktionen in die Methodentabelle für das Modul einfügen, das normalerweise in Ihrem Quellcode als nächstes kommt.
Die Methodenzuordnungstabelle
Diese Methodentabelle ist ein einfaches Array von PyMethodDef-Strukturen. Diese Struktur sieht in etwa so aus −
struct PyMethodDef { char *ml_name; PyCFunction ml_meth; int ml_flags; char *ml_doc; };
Hier ist die Beschreibung der Mitglieder dieser Struktur −
-
ml_name − Dies ist der Name der Funktion, wie sie der Python-Interpreter präsentiert, wenn er in Python-Programmen verwendet wird.
-
ml_meth − Dies muss die Adresse einer Funktion sein, die eine der im vorigen Abschnitt beschriebenen Signaturen hat.
-
ml_flags − Dies teilt dem Interpreter mit, welche der drei Signaturen ml_meth verwendet.
-
Dieses Flag hat normalerweise einen Wert von METH_VARARGS.
-
Dieses Flag kann mit METH_KEYWORDS bitweise ODER-verknüpft werden, wenn Sie Schlüsselwortargumente in Ihrer Funktion zulassen möchten.
-
Dies kann auch einen Wert von METH_NOARGS haben, was anzeigt, dass Sie keine Argumente akzeptieren möchten.
-
-
ml_doc − Dies ist der Docstring für die Funktion, der NULL sein könnte, wenn Sie keine Lust haben, einen zu schreiben.
Diese Tabelle muss mit einem Sentinel abgeschlossen werden, der aus NULL- und 0-Werten für die entsprechenden Elemente besteht.
Beispiel
Für die oben definierte Funktion haben wir folgende Methodenzuordnungstabelle −
static PyMethodDef module_methods[] = { { "func", (PyCFunction)module_func, METH_NOARGS, NULL }, { NULL, NULL, 0, NULL } };
Die Initialisierungsfunktion
Der letzte Teil Ihres Erweiterungsmoduls ist die Initialisierungsfunktion. Diese Funktion wird vom Python-Interpreter aufgerufen, wenn das Modul geladen wird. Es ist erforderlich, dass die Funktion initModule heißt , wobei Modul ist der Name des Moduls.
Die Initialisierungsfunktion muss aus der Bibliothek exportiert werden, die Sie erstellen werden. Die Python-Header definieren PyMODINIT_FUNC, um die entsprechenden Beschwörungsformeln für die jeweilige Umgebung, in der wir kompilieren, einzuschließen. Alles, was Sie tun müssen, ist es bei der Definition der Funktion zu verwenden.
Ihre C-Initialisierungsfunktion hat im Allgemeinen die folgende Gesamtstruktur −
PyMODINIT_FUNC initModule() { Py_InitModule3(func, module_methods, "docstring..."); }
Hier ist die Beschreibung von Py_InitModule3 Funktion −
-
Funktion − Dies ist die zu exportierende Funktion.
-
Modul _Methoden − Dies ist der oben definierte Mapping-Tabellenname.
-
docstring − Dies ist der Kommentar, den Sie in Ihrer Erweiterung angeben möchten.
Das alles zusammengenommen sieht wie folgt aus −
#include <Python.h> static 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..."); }
Beispiel
Ein einfaches Beispiel, das alle oben genannten Konzepte nutzt −
#include <Python.h> static 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!"); }
Hier der Py_BuildValue -Funktion wird verwendet, um einen Python-Wert zu erstellen. Speichern Sie den obigen Code in der Datei hello.c. Wir würden sehen, wie dieses Modul kompiliert und installiert wird, damit es vom Python-Skript aufgerufen wird.
Erweiterungen erstellen und installieren
Die distutils -Paket macht es sehr einfach, Python-Module, sowohl reine Python- als auch Erweiterungsmodule, auf standardmäßige Weise zu verteilen. Module werden in Quellform verteilt und über ein Setup-Skript namens setup.py erstellt und installiert wie folgt.
Für das obige Modul müssen Sie das folgende setup.py-Skript vorbereiten −
from distutils.core import setup, Extension setup(name='helloworld', version='1.0', \ ext_modules=[Extension('helloworld', ['hello.c'])])
Verwenden Sie nun den folgenden Befehl, der alle erforderlichen Kompilierungs- und Verknüpfungsschritte mit den richtigen Compiler- und Linker-Befehlen und -Flags durchführt, und kopieren Sie die resultierende dynamische Bibliothek in ein geeignetes Verzeichnis −
$ python setup.py install
Auf Unix-basierten Systemen müssen Sie diesen Befehl höchstwahrscheinlich als root ausführen, um Berechtigungen zum Schreiben in das Verzeichnis site-packages zu erhalten. Unter Windows ist dies normalerweise kein Problem.
Erweiterungen importieren
Sobald Sie Ihre Erweiterung installiert haben, können Sie diese Erweiterung wie folgt in Ihr Python-Skript importieren und aufrufen −
#!/usr/bin/python import helloworld print helloworld.helloworld()
Dies würde zu folgendem Ergebnis führen −
Hello, Python extensions!!
Funktionsparameter übergeben
Da Sie höchstwahrscheinlich Funktionen definieren wollen, die Argumente akzeptieren, können Sie eine der anderen Signaturen für Ihre C-Funktionen verwenden. Beispielsweise würde die folgende Funktion, die eine Reihe von Parametern akzeptiert, wie folgt definiert:−
static PyObject *module_func(PyObject *self, PyObject *args) { /* Parse args and do something interesting here. */ Py_RETURN_NONE; }
Die Methodentabelle, die einen Eintrag für die neue Funktion enthält, würde wie folgt aussehen −
static PyMethodDef module_methods[] = { { "func", (PyCFunction)module_func, METH_NOARGS, NULL }, { "func", module_func, METH_VARARGS, NULL }, { NULL, NULL, 0, NULL } };
Sie können die API PyArg_ParseTuple verwenden Funktion, um die Argumente aus dem einen PyObject-Zeiger zu extrahieren, der an Ihre C-Funktion übergeben wird.
Das erste Argument für PyArg_ParseTuple ist das Argument args. Dies ist das Objekt, das Sie parsen werden . Das zweite Argument ist eine Formatzeichenfolge, die die Argumente so beschreibt, wie Sie sie erwarten. Jedes Argument wird wie folgt durch ein oder mehrere Zeichen in der Formatzeichenfolge dargestellt.
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 here. */ Py_RETURN_NONE; }
Wenn Sie die neue Version Ihres Moduls kompilieren und importieren, können Sie die neue Funktion mit einer beliebigen Anzahl von Argumenten beliebigen Typs 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)
Wahrscheinlich fallen Ihnen noch mehr Variationen ein.
Das PyArg_ParseTuple Funktion
Hier ist die Standardsignatur für PyArg_ParseTuple Funktion −
int PyArg_ParseTuple(PyObject* tuple,char* format,...)
Diese Funktion gibt 0 für Fehler und einen Wert ungleich 0 für Erfolg zurück. Tupel ist das PyObject*, das das zweite Argument der C-Funktion war. Hier formatieren ist ein C-String, der obligatorische und optionale Argumente beschreibt.
Hier ist eine Liste von Formatcodes für PyArg_ParseTuple Funktion −
Code | C-Typ | Bedeutung |
---|---|---|
c | Zeichen | Ein Python-String der Länge 1 wird zu einem C-Zeichen. |
d | doppelt | Ein Python-Float wird zu einem C-Double. |
f | schweben | Ein Python-Float wird zu einem C-Float. |
ich | int | Ein Python-Int wird zu einem C-Int. |
l | lang | Ein Python int wird zu einem C long. |
L | lang lang | Ein Python int wird zu einem C long long |
O | PyObject* | Ruft eine geliehene Nicht-NULL-Referenz auf das Python-Argument ab. |
s | Zeichen* | Python-String ohne eingebettete Nullen in C-Zeichen*. |
s# | char*+int | Jeder Python-String an C-Adresse und Länge. |
t# | char*+int | Schreibgeschützter Einzelsegmentpuffer an C-Adresse und Länge. |
u | Py_UNICODE* | Python Unicode ohne eingebettete Nullen zu C. |
u# | Py_UNICODE*+int | Beliebige Python-Unicode-C-Adresse und Länge. |
w# | char*+int | Lesen/Schreiben des Einzelsegmentpuffers an C-Adresse und Länge. |
z | Zeichen* | Akzeptiert wie s auch None (setzt C char* auf NULL). |
z# | char*+int | Akzeptiert wie s# auch None (setzt C char* auf NULL). |
(...) | gemäß ... | Eine Python-Sequenz wird als ein Argument pro Element behandelt. |
| | Die folgenden Argumente sind optional. | |
: | Formatende, gefolgt von Funktionsname für Fehlermeldungen. | |
; | Ende formatieren, gefolgt vom gesamten Text der Fehlermeldung. |
Rückgabewerte
Py_BuildValue nimmt einen Formatstring ähnlich wie PyArg_ParseTuple an tut. Anstatt die Adressen der Werte, die Sie erstellen, zu übergeben, übergeben Sie die tatsächlichen Werte. Hier ist ein Beispiel, das zeigt, wie man eine add-Funktion implementiert −
static PyObject *foo_add(PyObject *self, PyObject *args) { int a; int b; if (!PyArg_ParseTuple(args, "ii", &a, &b)) { return NULL; } return Py_BuildValue("i", a + b); }
So würde es aussehen, wenn es in Python implementiert wäre −
def add(a, b): return (a + b)
Sie können zwei Werte von Ihrer Funktion wie folgt zurückgeben, dies würde mit einer Liste in Python erfasst werden.
static PyObject *foo_add_subtract(PyObject *self, PyObject *args) { int a; int b; if (!PyArg_ParseTuple(args, "ii", &a, &b)) { return NULL; } return Py_BuildValue("ii", a + b, a - b); }
So würde es aussehen, wenn es in Python implementiert wäre −
def add_subtract(a, b): return (a + b, a - b)
Der Py_BuildValue Funktion
Hier ist die Standardsignatur für Py_BuildValue Funktion −
PyObject* Py_BuildValue(char* format,...)
Hier formatieren ist ein C-String, der das zu erstellende Python-Objekt beschreibt. Die folgenden Argumente von Py_BuildValue sind C-Werte, aus denen das Ergebnis aufgebaut wird. Das PyObject* Ergebnis ist eine neue Referenz.
Die folgende Tabelle listet die häufig verwendeten Code-Strings auf, von denen null oder mehr zu einem String-Format verbunden sind.
Code | C-Typ | Bedeutung |
---|---|---|
c | Zeichen | Ein C-Zeichen wird zu einem Python-String der Länge 1. |
d | doppelt | Ein C-Double wird zu einem Python-Float. |
f | schweben | Ein C-Float wird zu einem Python-Float. |
ich | int | Ein C-int wird zu einem Python-int. |
l | lang | Ein C long wird zu einem Python int. |
N | PyObject* | Übergibt ein Python-Objekt und stiehlt eine Referenz. |
O | PyObject* | Übergibt ein Python-Objekt und INCREFs wie gewohnt. |
O& | umwandeln+aufheben* | Beliebige Umwandlung |
s | Zeichen* | C 0-terminiertes char* zu Python-String oder NULL zu None. |
s# | char*+int | C char* und Länge zu Python-String oder NULL zu None. |
u | Py_UNICODE* | C-weite, nullterminierte Zeichenfolge zu Python Unicode oder NULL zu None. |
u# | Py_UNICODE*+int | C-breiter String und Länge zu Python Unicode oder NULL zu None. |
w# | char*+int | Lesen/Schreiben des Einzelsegmentpuffers an C-Adresse und Länge. |
z | Zeichen* | Akzeptiert wie s auch None (setzt C char* auf NULL). |
z# | char*+int | Akzeptiert wie s# auch None (setzt C char* auf NULL). |
(...) | gemäß ... | Erzeugt Python-Tupel aus C-Werten. |
[...] | gemäß ... | Erstellt eine Python-Liste aus C-Werten. |
{...} | gemäß ... | Erzeugt ein Python-Wörterbuch aus C-Werten, abwechselnd Schlüsseln und Werten. |
Code {...} erstellt Wörterbücher aus einer geraden Anzahl von C-Werten, abwechselnd Schlüsseln und Werten. Beispielsweise gibt Py_BuildValue("{issi}",23,"zig","zag",42) ein Wörterbuch wie {23:'zig','zag':42} von Python zurück.
Python
- Python objektorientierte Programmierung
- Python Print()-Anweisung:Drucken mit Beispielen
- Python String strip() Funktion mit BEISPIEL
- Python String count() mit BEISPIELE
- Python String format() Erklären Sie mit BEISPIELE
- Python String find() Methode mit Beispielen
- Python-Lambda-Funktionen mit BEISPIELE
- Python-Funktion round() mit BEISPIELE
- Python map() Funktion mit BEISPIELE
- Python Timeit() mit Beispielen