Eine Übersetzung des Artikels wurde speziell für Studenten des Python Developer- Kurses erstellt .
Hinweis : Der Code in diesem Artikel ist unter GNU AGPLv3 lizenziert .Ich habe diesen Leitfaden geschrieben, weil ich keinen finden konnte, der alle nützlichen Dinge über ctypes kombiniert. Ich hoffe, dieser Artikel erleichtert jemandem das Leben erheblich.Inhalt:- Grundlegende Optimierungen
- Stile
- Python-Kompilierung
- Strukturen in Python
- Rufen Sie Ihren Code in C auf
- Pypy
Grundlegende Optimierungen
Berücksichtigen Sie vor dem Umschreiben des Python-Quellcodes in C die grundlegenden Optimierungsmethoden in Python.Integrierte Datenstrukturen
Die in Python integrierten Datenstrukturen wie set und dict sind in C geschrieben. Sie funktionieren viel schneller als Ihre eigenen Datenstrukturen, die als Python-Klassen geschrieben wurden. Weitere Datenstrukturen neben dem Standardsatz, dem Diktat, der Liste und dem Tupel sind in der Dokumentation des Sammlungsmoduls beschrieben .Listen Sie Ausdrücke auf
Verwenden Sie Listenausdrücke, anstatt Elemente mit der Standardmethode zur Liste hinzuzufügen.
mapped = []
for value in originallist:
mapped.append(myfunc(value))
mapped = [myfunc(value) in originallist]
ctypes
Mit dem Modul ctypes können Sie mit C-Code aus Python interagieren, ohne ein Modul subprocess
oder ein ähnliches Modul zum Starten anderer Prozesse über die CLI zu verwenden.Es gibt nur zwei Teile: Kompilieren von C-Code zum Laden in Qualität shared object
und Einrichten von Datenstrukturen in Python-Code, um sie den Typen C zuzuordnen.In diesem Artikel werde ich meinen Python-Code mit lcs.c verbinden , das die längste Teilsequenz in zwei findet Listen von Zeichenfolgen. Ich möchte, dass Folgendes in Python funktioniert:list1 = ['My', 'name', 'is', 'Sam', 'Stevens', '!']
list2 = ['My', 'name', 'is', 'Alex', 'Stevens', '.']
common = lcs(list1, list2)
print(common)
Ein Problem besteht darin, dass diese bestimmte C-Funktion die Signatur einer Funktion ist, die Listen von Zeichenfolgen als Argumenttypen verwendet und einen Typ zurückgibt, der keine feste Länge hat. Ich löse dieses Problem mit einer Sequenzstruktur, die Zeiger und Länge enthält.Kompilieren von C-Code in Python
Zunächst wird C-Quellcode ( lcs.c ) lcs.so
zum Laden in Python kompiliert .gcc -c -Wall -Werror -fpic -O3 lcs.c -o lcs.o
gcc -shared -o lcs.so lcs.o
- - Die Wand zeigt alle Warnungen an.
- - Werror wird alle Warnungen in Fehler einschließen ;
- - fpic generiert positionsunabhängige Anweisungen, die Sie benötigen, wenn Sie diese Bibliothek in Python verwenden möchten.
- - O3 maximiert die Optimierung;
Und jetzt werden wir beginnen, Python-Code unter Verwendung der resultierenden gemeinsam genutzten Objektdatei zu schreiben .Strukturen in Python
Unten sind zwei Datenstrukturen aufgeführt, die in meinem C-Code verwendet werden.struct Sequence
{
char **items;
int length;
};
struct Cell
{
int index;
int length;
struct Cell *prev;
};
Und hier ist die Übersetzung dieser Strukturen in Python.import ctypes
class SEQUENCE(ctypes.Structure):
_fields_ = [('items', ctypes.POINTER(ctypes.c_char_p)),
('length', ctypes.c_int)]
class CELL(ctypes.Structure):
pass
CELL._fields_ = [('index', ctypes.c_int), ('length', ctypes.c_int),
('prev', ctypes.POINTER(CELL))]
Ein paar Anmerkungen:- Alle Strukturen sind Klassen, die von erben
ctypes.Structure
. - Das einzige Feld
_fields_
ist eine Liste von Tupeln. Jedes Tupel ist ( <variable-name>
, <ctypes.TYPE>
). - Es
ctypes
gibt ähnliche Typen in c_char (char) und c_char_p (* char) . - Es gibt
ctypes
auch einen POINTER()
, der aus jedem übergebenen Typ einen Typzeiger erstellt. - Wenn Sie eine rekursive Definition wie in haben
CELL
, müssen Sie die ursprüngliche Deklaration übergeben und dann die Felder hinzufügen _fields_
, um später einen Link zu sich selbst zu erhalten. - Da ich
CELL
Python nicht in meinem Code verwendet habe, musste ich diese Struktur nicht schreiben, aber sie hat eine interessante Eigenschaft im rekursiven Feld.
Rufen Sie Ihren Code in C auf
Außerdem brauchte ich Code, um Python-Typen in neue Strukturen in C zu konvertieren. Jetzt können Sie Ihre neue C-Funktion verwenden, um Python-Code zu beschleunigen.def list_to_SEQUENCE(strlist: List[str]) -> SEQUENCE:
bytelist = [bytes(s, 'utf-8') for s in strlist]
arr = (ctypes.c_char_p * len(bytelist))()
arr[:] = bytelist
return SEQUENCE(arr, len(bytelist))
def lcs(s1: List[str], s2: List[str]) -> List[str]:
seq1 = list_to_SEQUENCE(s1)
seq2 = list_to_SEQUENCE(s2)
common = lcsmodule.lcs(ctypes.byref(seq1), ctypes.byref(seq2))[0]
ret = []
for i in range(common.length):
ret.append(common.items[i].decode('utf-8'))
lcsmodule.freeSequence(common)
return ret
lcsmodule = ctypes.cdll.LoadLibrary('lcsmodule/lcs.so')
lcsmodule.lcs.restype = ctypes.POINTER(SEQUENCE)
list1 = ['My', 'name', 'is', 'Sam', 'Stevens', '!']
list2 = ['My', 'name', 'is', 'Alex', 'Stevens', '.']
common = lcs(list1, list2)
print(common)
Ein paar Anmerkungen:**char
(Liste der Zeichenfolgen) stimmt direkt mit einer Liste der Bytes in Python überein.- Es
lcs.c
gibt eine Funktion lcs()
mit der Signatur struct Sequence * lcs (struct Sequence * s1, struct Sequence * s2) . Um den Rückgabetyp einzurichten, verwende ich lcsmodule.lcs.restype = ctypes.POINTER(SEQUENCE)
. - Um einen Aufruf mit Bezug auf die Sequenzstruktur zu tätigen , verwende ich
ctypes.byref()
einen, der einen „Lichtzeiger“ auf Ihr Objekt zurückgibt (schneller als ctypes.POINTER()
). common.items
- Dies ist eine Liste von Bytes, die dekodiert werden können, um ret
in Form einer Liste zu erhalten str
.- lcsmodule.freeSequence (common) gibt nur den mit common verknüpften Speicher frei. Dies ist wichtig, da der Garbage Collector (AFAIK) ihn nicht automatisch sammelt.
Optimierter Python-Code ist Code, den Sie in C geschrieben und in Python eingeschlossen haben.Etwas mehr: PyPy
Achtung: Ich selbst habe PyPy noch nie benutzt.Eine der einfachsten Optimierungen besteht darin, Ihre Programme in der PyPy- Laufzeit auszuführen , die einen JIT-Compiler (Just-in-Time) enthält, der die Arbeit von Schleifen beschleunigt und sie zur wiederholten Ausführung in Maschinencode kompiliert.Wenn Sie Kommentare haben oder etwas besprechen möchten, schreiben Sie mir (samuel.robert.stevens@gmail.com).Das ist alles. Wir sehen uns auf dem Kurs !