Industrielle Fertigung
Industrielles Internet der Dinge | Industrielle Materialien | Gerätewartung und Reparatur | Industrielle Programmierung |
home  MfgRobots >> Industrielle Fertigung >  >> Industrial programming >> Python

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.

.so (Shared Object). Auf Windows typischerweise .dll (Dynamically Linked Library).

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:

  1. Header-Datei Python.h.
  2. C-Funktionen, die Sie als Interface vom Modul exponieren wollen.
  3. Tabelle, die Funktionsnamen (wie Python-Entwickler sie sehen) auf C-Funktionen im Modul abbildet.
  4. 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_meth verwendet. Meist METH_VARARGS. Kann mit METH_KEYWORDS per bitweisem OR kombiniert werden für Keyword-Argumente. METH_NOARGS für keine Argumente.
  • ml_doc – Docstring für die Funktion (kann NULL sein).

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

#include 
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...");
}

Einfaches Beispiel: Hello World

#include 
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!");
}

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

CodeC-TypBedeutung
ccharPython-String Länge 1 → C char
ddoublePython float → C double
ffloatPython float → C float
iintPython int → C int
llongPython int → C long
Llong longPython int → C long long
OPyObject*Non-NULL borrowed reference
schar*Python-String ohne embedded nulls → C char*
s#char*+intBeliebiger Python-String → C Adresse und Länge
t#char*+intRead-only single-segment buffer → C Adresse und Länge
uPy_UNICODE*Python Unicode ohne embedded nulls → C
u#Py_UNICODE*+intBeliebiges Python Unicode → C Adresse und Länge
w#char*+intRead/write single-segment buffer → C Adresse und Länge
zchar*Wie s, akzeptiert auch None (setzt C char* auf NULL)
z#char*+intWie 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

CodeC-TypBedeutung
ccharC char → Python-String Länge 1
ddoubleC double → Python float
ffloatC float → Python float
iintC int → Python int
llongC long → Python int
NPyObject*Python-Objekt übergeben, Referenz stehlen
OPyObject*Python-Objekt übergeben, INCREF wie normal
O&convert+void*Beliebige Konvertierung
schar*C 0-terminierter char* → Python-String, NULL → None
s#char*+intC char* und Länge → Python-String, NULL → None
uPy_UNICODE*C-breiter, null-terminierter String → Python Unicode, NULL → None
u#Py_UNICODE*+intC-breiter String und Länge → Python Unicode, NULL → None
w#char*+intRead/write single-segment buffer → C Adresse und Länge
zchar*Wie s, akzeptiert None (setzt C char* auf NULL)
z#char*+intWie 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

  1. Python Objektorientierte Programmierung meistern: Klassen, Vererbung, Kapselung & Polymorphie
  2. Python print()-Funktion: Praktischer Leitfaden mit Beispielen
  3. Pythons strip()-Methode meistern: Umfassender Leitfaden & Praxisbeispiele
  4. Pythons str.count(): Zeichen & Teilstrings zählen mit Beispielen
  5. Python format()-Strings meistern mit klaren Beispielen
  6. Pythons String.find()-Methode meistern: Syntax, Beispiele & Alternativen
  7. Python Lambda-Funktionen meistern: Praxisbeispiele & Best Practices
  8. Python round()-Funktion erklärt mit praktischen Beispielen
  9. Pythons map()-Funktion meistern: Syntax, Beispiele und Best Practices
  10. Python timeit(): Ausführungszeit messen mit praktischen Beispielen