Eine C-Bibliothek in Python einbinden: C, Cython oder ctypes?

284

Ich möchte eine C-Bibliothek aus einer Python-Anwendung aufrufen. Ich möchte nicht die gesamte API einschließen, sondern nur die Funktionen und Datentypen, die für meinen Fall relevant sind. Aus meiner Sicht habe ich drei Möglichkeiten:

  1. Erstellen Sie ein tatsächliches Erweiterungsmodul in C. Wahrscheinlich übertrieben, und ich möchte auch den Aufwand für das Erlernen des Schreibens von Erweiterungen vermeiden.
  2. Verwenden Sie Cython , um die relevanten Teile aus der C-Bibliothek für Python verfügbar zu machen.
  3. Machen Sie das Ganze in Python und ctypeskommunizieren Sie mit der externen Bibliothek.

Ich bin mir nicht sicher, ob 2) oder 3) die bessere Wahl ist. Der Vorteil von 3) ist, dass ctypeses Teil der Standardbibliothek ist und der resultierende Code reines Python wäre - obwohl ich nicht sicher bin, wie groß dieser Vorteil tatsächlich ist.

Gibt es bei beiden Möglichkeiten weitere Vor- und Nachteile? Welchen Ansatz empfehlen Sie?


Bearbeiten: Vielen Dank für all Ihre Antworten, sie bieten eine gute Ressource für alle, die etwas Ähnliches tun möchten. Die Entscheidung muss natürlich noch für den Einzelfall getroffen werden - es gibt keine Antwort auf die Frage "Das ist das Richtige". Für meinen eigenen Fall werde ich wahrscheinlich mit ctypes arbeiten, aber ich freue mich auch darauf, Cython in einem anderen Projekt auszuprobieren.

Da es keine einzige wahre Antwort gibt, ist es etwas willkürlich, eine zu akzeptieren. Ich habe mich für die Antwort von FogleBird entschieden, da sie einen guten Einblick in ctypes bietet und derzeit auch die Antwort mit der höchsten Bewertung ist. Ich empfehle jedoch, alle Antworten zu lesen, um einen guten Überblick zu erhalten.

Danke noch einmal.

balpha
quelle
3
In gewissem Maße kann die spezifische Anwendung (was die Bibliothek tut) die Wahl des Ansatzes beeinflussen. Wir haben ctypes recht erfolgreich verwendet, um mit vom Hersteller bereitgestellten DLLs für verschiedene Hardare-Teile (z. B. Oszilloskope) zu kommunizieren, aber ich würde ctypes nicht unbedingt zuerst auswählen, um mit einer numerischen Verarbeitungsbibliothek zu sprechen, da der Overhead gegenüber Cython oder SWIG höher ist.
Peter Hansen
1
Jetzt haben Sie, wonach Sie gesucht haben. Vier verschiedene Antworten (jemand hat auch SWIG gefunden). Das bedeutet, dass Sie jetzt 4 statt 3 Möglichkeiten haben.
Luka Rahne
@ralu Das habe ich auch gedacht :-) Aber im Ernst, ich habe nicht erwartet (oder wollte) eine Pro / Contra-Tabelle oder eine einzige Antwort mit der Aufschrift "Hier ist, was Sie tun müssen". Jede Frage zur Entscheidungsfindung wird am besten mit "Fans" jeder möglichen Wahl beantwortet, die ihre Gründe angeben. Das Community-Voting leistet dann seinen Beitrag, ebenso wie meine eigene Arbeit (die Argumente betrachten, auf meinen Fall anwenden, bereitgestellte Quellen lesen usw.). Lange Rede, kurzer Sinn: Hier gibt es einige gute Antworten.
Balpha
Also, mit welchem ​​Ansatz werden Sie gehen? :)
FogleBird
1
Soweit ich weiß (bitte korrigieren Sie mich, wenn ich falsch liege), ist Cython eine Gabelung von Pyrex mit mehr Entwicklung, was Pyrex ziemlich veraltet macht.
Balpha

Antworten:

115

ctypes ist die beste Wahl, um es schnell zu erledigen, und es ist eine Freude, mit Ihnen zu arbeiten, während Sie noch Python schreiben!

Ich habe kürzlich einen FTDI- Treiber für die Kommunikation mit einem USB-Chip über ctypes eingepackt und es war großartig. Ich hatte alles erledigt und arbeitete in weniger als einem Arbeitstag. (Ich habe nur die Funktionen implementiert, die wir brauchten, ungefähr 15 Funktionen).

Wir haben zuvor ein Drittanbieter-Modul, PyUSB , für denselben Zweck verwendet. PyUSB ist ein aktuelles C / Python-Erweiterungsmodul. Aber PyUSB hat die GIL nicht freigegeben, als Lese- / Schreibvorgänge blockiert wurden, was uns Probleme bereitete. Also habe ich unser eigenes Modul mit ctypes geschrieben, das die GIL beim Aufrufen der nativen Funktionen freigibt.

Beachten Sie, dass ctypes nicht über #defineKonstanten und Inhalte in der von Ihnen verwendeten Bibliothek Bescheid weiß, sondern nur über die Funktionen. Daher müssen Sie diese Konstanten in Ihrem eigenen Code neu definieren.

Hier ist ein Beispiel dafür, wie der Code letztendlich aussah (viele wurden herausgeschnitten und versuchten nur, Ihnen das Wesentliche zu zeigen):

from ctypes import *

d2xx = WinDLL('ftd2xx')

OK = 0
INVALID_HANDLE = 1
DEVICE_NOT_FOUND = 2
DEVICE_NOT_OPENED = 3

...

def openEx(serial):
    serial = create_string_buffer(serial)
    handle = c_int()
    if d2xx.FT_OpenEx(serial, OPEN_BY_SERIAL_NUMBER, byref(handle)) == OK:
        return Handle(handle.value)
    raise D2XXException

class Handle(object):
    def __init__(self, handle):
        self.handle = handle
    ...
    def read(self, bytes):
        buffer = create_string_buffer(bytes)
        count = c_int()
        if d2xx.FT_Read(self.handle, buffer, bytes, byref(count)) == OK:
            return buffer.raw[:count.value]
        raise D2XXException
    def write(self, data):
        buffer = create_string_buffer(data)
        count = c_int()
        bytes = len(data)
        if d2xx.FT_Write(self.handle, buffer, bytes, byref(count)) == OK:
            return count.value
        raise D2XXException

Jemand hat einige Benchmarks für die verschiedenen Optionen durchgeführt.

Ich könnte zögerlicher sein, wenn ich eine C ++ - Bibliothek mit vielen Klassen / Vorlagen / etc. Umschließen müsste. Aber ctypes funktioniert gut mit structs und kann sogar Rückruf in Python.

FogleBird
quelle
5
Schließen Sie sich dem Lob für ctypes an, aber beachten Sie ein (undokumentiertes) Problem: ctypes unterstützt kein Forking. Wenn Sie einen Prozess mit ctypes verlassen und sowohl übergeordnete als auch untergeordnete Prozesse weiterhin ctypes verwenden, werden Sie auf einen bösen Fehler stoßen, der mit ctypes unter Verwendung des gemeinsam genutzten Speichers zu tun hat.
Oren Shemesh
1
@OrenShemesh Gibt es weitere Informationen zu diesem Thema, auf die Sie mich hinweisen können? Ich denke, ich bin mit einem Projekt, an dem ich gerade arbeite, sicher, da ich glaube, dass nur der übergeordnete Prozess ctypes(für pyinotify) verwendet, aber ich möchte das Problem genauer verstehen.
Zigg
Diese Passage hilft mir sehr. One thing to note is that ctypes won't know about #define constants and stuff in the library you're using, only the functions, so you'll have to redefine those constants in your own code.Also muss ich Konstanten definieren, die in winioctl.h...
swdev
Wie wäre es mit Leistung? ctypesist viel langsamer als die C-Erweiterung, da der Engpass die Schnittstelle von Python zu C ist
TomSawyer
154

Warnung: Die Meinung eines Cython-Kernentwicklers liegt vor uns.

Ich empfehle Cython fast immer gegenüber ctypes. Der Grund ist, dass es einen viel reibungsloseren Upgrade-Pfad gibt. Wenn Sie ctypes verwenden, werden viele Dinge zunächst einfach sein, und es ist sicherlich cool, Ihren FFI-Code in einfachem Python zu schreiben, ohne zu kompilieren, Abhängigkeiten zu erstellen und so weiter. Irgendwann werden Sie jedoch mit ziemlicher Sicherheit feststellen, dass Sie häufig in Ihrer C-Bibliothek aufrufen müssen, entweder in einer Schleife oder in einer längeren Reihe von voneinander abhängigen Aufrufen, und Sie möchten dies beschleunigen. An diesem Punkt werden Sie feststellen, dass Sie dies mit ctypes nicht tun können. Wenn Sie Rückruffunktionen benötigen und feststellen, dass Ihr Python-Rückrufcode zu einem Engpass wird, möchten Sie ihn beschleunigen und / oder auch nach C verschieben. Auch dies können Sie mit ctypes nicht tun.

Mit Cython, OTOH, können Sie den Umbruch- und Aufrufcode so dünn oder dick gestalten, wie Sie möchten. Sie können mit einfachen Aufrufen Ihres C-Codes aus normalem Python-Code beginnen, und Cython übersetzt sie in native C-Aufrufe, ohne zusätzlichen Aufrufaufwand und mit einem äußerst geringen Konvertierungsaufwand für Python-Parameter. Wenn Sie feststellen, dass Sie an einem Punkt, an dem Sie zu viele teure Aufrufe in Ihre C-Bibliothek tätigen, noch mehr Leistung benötigen, können Sie Ihren umgebenden Python-Code mit statischen Typen versehen und Cython ihn direkt in C für Sie optimieren lassen. Sie können auch Teile Ihres C-Codes in Cython neu schreiben, um Aufrufe zu vermeiden und Ihre Schleifen algorithmisch zu spezialisieren und zu straffen. Und wenn Sie einen schnellen Rückruf benötigen, Schreiben Sie einfach eine Funktion mit der entsprechenden Signatur und übergeben Sie sie direkt an die C-Rückrufregistrierung. Auch hier kein Overhead, und Sie erhalten eine einfache C-Anrufleistung. Und in dem viel weniger wahrscheinlichen Fall, dass Sie Ihren Code in Cython wirklich nicht schnell genug erhalten können, können Sie dennoch in Betracht ziehen, die wirklich kritischen Teile davon in C (oder C ++ oder Fortran) neu zu schreiben und ihn natürlich und nativ aus Ihrem Cython-Code aufzurufen. Aber dann wird dies wirklich der letzte Ausweg statt der einzigen Option.

Ctypes ist also nett, einfache Dinge zu tun und schnell etwas zum Laufen zu bringen. Sobald die Dinge jedoch anfangen zu wachsen, werden Sie höchstwahrscheinlich an den Punkt kommen, an dem Sie feststellen, dass Sie Cython von Anfang an besser verwenden sollten.

Stefan Behnel
quelle
4
+1 das sind gute Punkte, vielen Dank! Obwohl ich mich frage, ob das Verschieben nur der Engpassteile nach Cython wirklich so viel Aufwand bedeutet. Aber ich stimme zu, wenn Sie irgendwelche Leistungsprobleme erwarten, können Sie Cython genauso gut von Anfang an nutzen.
Balpha
Gilt dies immer noch für Programmierer, die sowohl mit C als auch mit Python vertraut sind? In diesem Fall kann man argumentieren, dass Python / ctypes die bessere Wahl ist, da die Vektorisierung von C-Schleifen (SIMD) manchmal einfacher ist. Ansonsten kann ich mir keine Cython-Nachteile vorstellen.
Alex van Houten
Danke für die Antwort! Eine Sache, mit der ich Probleme mit Cython hatte, ist, den Build-Prozess richtig zu machen (aber das hat auch damit zu tun, dass ich noch nie zuvor ein Python-Modul geschrieben habe) - sollte ich es vorher kompilieren oder Cython-Quelldateien in sdist und ähnliche Fragen aufnehmen. Ich habe einen Blog-Beitrag darüber geschrieben, falls jemand ähnliche Probleme / Zweifel hat: martinsosic.com/development/2016/02/08/…
Martinsos
Danke für die Antwort! Ein Nachteil bei der Verwendung von Cython ist, dass die Überladung von Operatoren nicht vollständig implementiert ist (z __radd__. B. ). Dies ist besonders ärgerlich, wenn Sie planen, dass Ihre Klasse mit integrierten Typen (z . B. intund float) interagiert . Auch magische Methoden in Cython sind im Allgemeinen nur ein bisschen fehlerhaft.
Monolith
100

Cython ist an sich schon ein ziemlich cooles Tool, das es wert ist, erlernt zu werden, und das der Python-Syntax überraschend nahe kommt. Wenn Sie mit Numpy wissenschaftlich rechnen, ist Cython der richtige Weg, da es für schnelle Matrixoperationen in Numpy integriert ist.

Cython ist eine Obermenge der Python-Sprache. Sie können jede gültige Python-Datei darauf werfen, und es wird ein gültiges C-Programm ausgespuckt. In diesem Fall ordnet Cython die Python-Aufrufe nur der zugrunde liegenden CPython-API zu. Dies führt zu einer Beschleunigung von möglicherweise 50%, da Ihr Code nicht mehr interpretiert wird.

Um einige Optimierungen zu erhalten, müssen Sie Cython zusätzliche Fakten zu Ihrem Code mitteilen, z. B. Typdeklarationen. Wenn Sie es genug sagen, kann es den Code auf reines C reduzieren. Das heißt, eine for-Schleife in Python wird zu einer for-Schleife in C. Hier sehen Sie massive Geschwindigkeitsgewinne. Sie können hier auch auf externe C-Programme verlinken.

Die Verwendung von Cython-Code ist ebenfalls unglaublich einfach. Ich dachte, das Handbuch macht es schwierig. Sie tun buchstäblich nur:

$ cython mymodule.pyx
$ gcc [some arguments here] mymodule.c -o mymodule.so

und dann können Sie import mymodulein Ihrem Python-Code ganz vergessen, dass es bis zu C kompiliert.

Da Cython so einfach einzurichten und zu verwenden ist, empfehle ich auf jeden Fall, es zu versuchen, um festzustellen, ob es Ihren Anforderungen entspricht. Es wird keine Verschwendung sein, wenn sich herausstellt, dass es nicht das Werkzeug ist, nach dem Sie suchen.

Carl
quelle
1
Kein Problem. Das Schöne an Cython ist, dass Sie nur lernen können, was Sie brauchen. Wenn Sie nur eine bescheidene Verbesserung wünschen, müssen Sie nur Ihre Python-Dateien kompilieren und fertig.
Carl
18
"Sie können jede gültige Python-Datei darauf werfen, und es wird ein gültiges C-Programm ausgespuckt." <- Nicht ganz, es gibt einige Einschränkungen: docs.cython.org/src/userguide/limitations.html Wahrscheinlich kein Problem für die meisten Anwendungsfälle, wollte aber nur vollständig sein.
Randy Syring
7
Die Probleme werden mit jeder Version geringer, bis auf dieser Seite jetzt steht "Die meisten Probleme wurden in 0.15 gelöst".
Henry Gomersall
3
Außerdem gibt es eine noch einfachere Möglichkeit, Cython-Code zu importieren: Schreiben Sie Ihren Cython-Code als mymod.pyxModul und tun Sie dies, import pyximport; pyximport.install(); import mymodund die Kompilierung erfolgt hinter den Kulissen.
Kaushik Ghose
3
@kaushik Noch einfacher ist pypi.python.org/pypi/runcython . Einfach benutzen runcython mymodule.pyx. Und im Gegensatz zu pyximport können Sie es für anspruchsvollere Verknüpfungsaufgaben verwenden. Die einzige Einschränkung ist, dass ich derjenige bin, der die 20 Zeilen Bash dafür geschrieben hat und möglicherweise voreingenommen ist.
RussellStewart
42

Zum Aufrufen einer C-Bibliothek aus einer Python-Anwendung gibt es auch cffi , eine neue Alternative für ctypes . Es bringt einen frischen Look für FFI:

  • es behandelt das Problem auf faszinierende, saubere Weise (im Gegensatz zu ctypes )
  • Es muss kein Nicht-Python-Code geschrieben werden (wie in SWIG, Cython , ...).
Robert Zaremba
quelle
definitiv der richtige Weg zum Verpacken , wie OP es wollte. cython klingt großartig, um sie selbst als Hot Loops zu schreiben, aber für Schnittstellen ist cffi einfach ein direktes Upgrade von ctypes.
fliegende Schafe
21

Ich werfe noch einen raus: SWIG

Es ist leicht zu lernen, macht viele Dinge richtig und unterstützt viel mehr Sprachen, so dass die Zeit, die Sie damit verbringen, es zu lernen, ziemlich nützlich sein kann.

Wenn Sie SWIG verwenden, erstellen Sie ein neues Python-Erweiterungsmodul, aber SWIG erledigt den größten Teil des schweren Hebens für Sie.

Chris Arguin
quelle
18

Persönlich würde ich ein Erweiterungsmodul in C schreiben. Lassen Sie sich nicht von Python C-Erweiterungen einschüchtern - sie sind überhaupt nicht schwer zu schreiben. Die Dokumentation ist sehr klar und hilfreich. Als ich zum ersten Mal eine C-Erweiterung in Python schrieb, brauchte ich ungefähr eine Stunde, um herauszufinden, wie man eine schreibt - nicht viel Zeit.

Mipadi
quelle
Eine C-Bibliothek einpacken. Den Code finden Sie hier: github.com/mdippery/lehmer
mipadi
1
@forivall: Der Code war nicht wirklich nützlich, und es gibt bessere Zufallszahlengeneratoren. Ich habe nur ein Backup auf meinem Computer.
Mipadi
2
Einverstanden. Die C-API von Python ist bei weitem nicht so beängstigend wie sie aussieht (vorausgesetzt, Sie kennen C). Im Gegensatz zu Python und seinem Reservoir an Bibliotheken, Ressourcen und Entwicklern sind Sie beim Schreiben von Erweiterungen in C im Grunde genommen allein. Wahrscheinlich der einzige Nachteil (außer denjenigen, die normalerweise mit dem Schreiben in C einhergehen).
Noob Saibot
1
@mipadi: Nun, aber sie unterscheiden sich zwischen Python 2.x und 3.x. Es ist daher bequemer, Cython zum Schreiben Ihrer Erweiterung zu verwenden, Cython alle Details herauszufinden und dann den generierten C-Code für Python 2.x oder zu kompilieren 3.x nach Bedarf.
0xC0000022L
2
@mipadi Es scheint, dass der Github-Link tot ist und auf archive.org nicht verfügbar zu sein scheint. Haben Sie ein Backup?
JRH
11

ctypes ist großartig, wenn Sie bereits einen kompilierten Bibliotheks-Blob haben (z. B. Betriebssystembibliotheken). Der Anrufaufwand ist jedoch hoch. Wenn Sie also viele Anrufe in die Bibliothek tätigen und den C-Code trotzdem schreiben (oder zumindest kompilieren), würde ich sagen, dass Sie sich dafür entscheiden sollten Cython . Es ist nicht viel mehr Arbeit und es wird viel schneller und pythonischer sein, die resultierende pyd-Datei zu verwenden.

Ich persönlich neige dazu, Cython für eine schnelle Beschleunigung des Python-Codes zu verwenden (Schleifen und Ganzzahlvergleiche sind zwei Bereiche, in denen Cython besonders gut zur Geltung kommt) . Das Einrichten von Boost.Python kann schwierig sein, aber sobald es funktioniert, ist das Umschließen von C / C ++ - Code unkompliziert.

cython ist auch großartig darin, numpy zu verpacken (was ich aus dem SciPy 2009-Verfahren gelernt habe ), aber ich habe numpy nicht verwendet, daher kann ich das nicht kommentieren.

Ryan Ginstrom
quelle
11

Wenn Sie bereits eine Bibliothek mit einer definierten API haben, ist dies meiner Meinung ctypesnach die beste Option, da Sie nur eine kleine Initialisierung durchführen und die Bibliothek dann mehr oder weniger so aufrufen müssen, wie Sie es gewohnt sind.

Ich denke, Cython oder das Erstellen eines Erweiterungsmoduls in C (was nicht sehr schwierig ist) sind nützlicher, wenn Sie neuen Code benötigen, z. B. diese Bibliothek aufrufen und einige komplexe, zeitaufwändige Aufgaben ausführen und das Ergebnis dann an Python übergeben.

Ein anderer Ansatz für einfache Programme besteht darin, direkt einen anderen Prozess (extern kompiliert) auszuführen, das Ergebnis an die Standardausgabe auszugeben und es mit dem Unterprozessmodul aufzurufen. Manchmal ist es der einfachste Ansatz.

Zum Beispiel, wenn Sie ein Konsolen-C-Programm erstellen, das mehr oder weniger so funktioniert

$miCcode 10
Result: 12345678

Man könnte es von Python aus aufrufen

>>> import subprocess
>>> p = subprocess.Popen(['miCcode', '10'], shell=True, stdout=subprocess.PIPE)
>>> std_out, std_err = p.communicate()
>>> print std_out
Result: 12345678

Mit einer kleinen String-Formatierung können Sie das Ergebnis beliebig erfassen. Sie können auch die Standardfehlerausgabe erfassen, sodass sie sehr flexibel ist.

Khelben
quelle
Obwohl diese Antwort nichts Falsches enthält, sollten die shell=TrueBenutzer vorsichtig sein, wenn der Code für den Zugriff durch andere geöffnet werden soll, da das Aufrufen von Unterprozessen mit leicht zu einem Exploit führen kann, wenn ein Benutzer wirklich eine Shell erhält. Es ist in Ordnung, wenn der Entwickler der einzige Benutzer ist, aber auf der Welt gibt es eine ganze Reihe nerviger Stiche, die nur auf so etwas warten.
Ben
7

Es gibt ein Problem, bei dem ich ctypes und nicht cython verwendet habe und das in anderen Antworten nicht erwähnt wird.

Bei Verwendung von ctypes hängt das Ergebnis überhaupt nicht vom verwendeten Compiler ab. Sie können eine Bibliothek mit mehr oder weniger jeder Sprache schreiben, die in eine native gemeinsam genutzte Bibliothek kompiliert werden kann. Es spielt keine Rolle, welches System, welche Sprache und welcher Compiler. Cython ist jedoch durch die Infrastruktur begrenzt. Wenn Sie beispielsweise den Intel-Compiler unter Windows verwenden möchten, ist es viel schwieriger, Cython zum Laufen zu bringen: Sie sollten Cython den Compiler "erklären", etwas mit diesem genauen Compiler neu kompilieren usw. Dies schränkt die Portabilität erheblich ein.

Mischa
quelle
4

Wenn Sie auf Windows abzielen und einige proprietäre C ++ - Bibliotheken umschließen, werden Sie möglicherweise bald feststellen, dass verschiedene Versionen von msvcrt***.dll(Visual C ++ Runtime) leicht inkompatibel sind.

Dies bedeutet, dass Sie möglicherweise nicht verwenden können, Cythonda das Ergebnis wrapper.pydmit msvcr90.dll (Python 2.7) oder msvcr100.dll (Python 3.x) verknüpft ist . Wenn die Bibliothek, die Sie umschließen, mit einer anderen Laufzeitversion verknüpft ist, haben Sie kein Glück.

Damit dies funktioniert, müssen Sie C-Wrapper für C ++ - Bibliotheken erstellen und diese Wrapper-DLL mit derselben Version msvcrt***.dllwie Ihre C ++ - Bibliothek verknüpfen . Und dann verwenden Sie ctypes, um Ihre handgerollte Wrapper-DLL zur Laufzeit dynamisch zu laden.

Es gibt also viele kleine Details, die im folgenden Artikel ausführlich beschrieben werden:

"Schöne native Bibliotheken (in Python) ": http://lucumr.pocoo.org/2013/8/18/beautiful-native-libraries/

iljau
quelle
Dieser Artikel hat nichts mit den Problemen zu tun, die Sie mit der Kompatibilität von Microsoft-Compilern ansprechen. Es ist wirklich nicht sehr schwierig, Cython-Erweiterungen unter Windows zum Laufen zu bringen. Ich konnte MinGW für so ziemlich alles verwenden. Eine gute Python-Distribution hilft jedoch.
IanH
2
+1 für die Erwähnung eines möglichen Problems unter Windows (das ich derzeit auch habe ...). @IanH Es geht weniger um Windows im Allgemeinen, aber es ist ein Chaos, wenn Sie mit einer bestimmten Drittanbieter-Bibliothek stecken bleiben, die nicht zu Ihrer Python-Distribution passt.
Sebastian
2

Ich weiß, dass dies eine alte Frage ist, aber diese Sache taucht bei Google auf, wenn Sie nach Dingen wie suchen ctypes vs cython, und die meisten Antworten hier stammen von Personen, die bereits über ausreichende Kenntnisse verfügen cythonoder cmöglicherweise nicht die tatsächliche Zeit widerspiegeln, die Sie für das Erlernen dieser Aufgaben benötigt haben um Ihre Lösung zu implementieren. Ich bin ein absoluter Anfänger in beiden. Ich habe noch nie berührt cythonund habe sehr wenig Erfahrung c/c++.

In den letzten zwei Tagen suchte ich nach einer Möglichkeit, einen leistungsintensiven Teil meines Codes an etwas Niedrigeres als Python zu delegieren. Ich habe meinen Code sowohl in ctypesals auch implementiert Cython, der im Wesentlichen aus zwei einfachen Funktionen bestand.

Ich hatte eine riesige String-Liste , die verarbeitet werden musste. Hinweis listund string. Beide Typen entsprechen nicht perfekt den Typen in c, da Python-Strings standardmäßig Unicode sind und cStrings nicht. Listen in Python sind einfach KEINE Arrays von c.

Hier ist mein Urteil. Verwenden Sie cython. Es lässt sich flüssiger in Python integrieren und ist im Allgemeinen einfacher zu handhaben. Wenn etwas schief geht, wird ctypesnur ein Segfault ausgelöst, und Sie erhalten zumindest cythonKompilierungswarnungen mit einem Stack-Trace, wann immer dies möglich ist, und Sie können problemlos ein gültiges Python-Objekt zurückgeben cython.

Hier ist ein detaillierter Bericht darüber, wie viel Zeit ich in beide investieren musste, um dieselbe Funktion zu implementieren. Ich habe übrigens sehr wenig C / C ++ programmiert:

  • C-Typen:

    • Ungefähr 2 Stunden, um zu untersuchen, wie ich meine Liste der Unicode-Zeichenfolgen in einen AC-kompatiblen Typ umwandeln kann.
    • Etwa eine Stunde, um eine Zeichenfolge ordnungsgemäß von der AC-Funktion zurückzugeben. Hier habe ich tatsächlich meine eigene Lösung für SO bereitgestellt, nachdem ich die Funktionen geschrieben habe.
    • Etwa eine halbe Stunde, um den Code in c zu schreiben, kompilieren Sie ihn in eine dynamische Bibliothek.
    • 10 Minuten, um einen Testcode in Python zu schreiben und zu überprüfen, ob der cCode funktioniert.
    • Etwa eine Stunde, um einige Tests durchzuführen und den cCode neu zu ordnen .
    • Dann habe ich den cCode in die eigentliche Codebasis eingesteckt und festgestellt , dass ctypesdies mit dem multiprocessingModul nicht gut funktioniert, da sein Handler standardmäßig nicht auswählbar ist.
    • Ungefähr 20 Minuten lang habe ich meinen Code neu angeordnet, um das multiprocessingModul nicht zu verwenden , und es erneut versucht.
    • Dann cerzeugte die zweite Funktion in meinem Code Segfaults in meiner Codebasis, obwohl sie meinen Testcode bestand. Nun, dies ist wahrscheinlich meine Schuld daran, dass ich nicht gut mit Randfällen umgegangen bin. Ich suchte nach einer schnellen Lösung.
    • Etwa 40 Minuten lang habe ich versucht, mögliche Ursachen für diese Fehler zu ermitteln.
    • Ich habe meine Funktionen in zwei Bibliotheken aufgeteilt und es erneut versucht. Hatte noch Segfaults für meine zweite Funktion.
    • Ich entschied mich, die zweite Funktion loszulassen und nur die erste Funktion des cCodes zu verwenden, und bei der zweiten oder dritten Iteration der Python-Schleife, die sie verwendet, hatte UnicodeErrorich das Problem, an keiner Stelle ein Byte zu decodieren, obwohl ich alles codiert und decodiert habe explizit.

An diesem Punkt entschied ich mich, nach einer Alternative zu suchen und untersuchte cython:

  • Cython
    • 10 Minuten Lesen von Cython Hallo Welt .
    • 15 Minuten SO überprüfen, wie Cython mit setuptoolsanstelle von verwendet wird distutils.
    • 10 Minuten Lesen über Cython-Typen und Python-Typen. Ich habe gelernt, dass ich die meisten eingebauten Python-Typen für die statische Typisierung verwenden kann.
    • 15 Minuten, um meinen Python-Code mit Cython-Typen neu zu notieren.
    • 10 Minuten Ändern von my setup.py, um ein kompiliertes Modul in meiner Codebasis zu verwenden.
    • Steckte das Modul direkt in die multiprocessingVersion der Codebasis. Es klappt.

Für die Aufzeichnung habe ich natürlich nicht den genauen Zeitpunkt meiner Investition gemessen. Es kann durchaus sein, dass meine Wahrnehmung der Zeit aufgrund der mentalen Anstrengung, die erforderlich war, während ich mich mit ctypes befasste, etwas zu aufmerksam war. Aber es sollte das Gefühl vermitteln, mit cythonund umzugehenctypes

Kaan E.
quelle