PythoC ist eine Alternative zu Cython, aber mit mehr Features und Flexibilität. So arbeiten Sie damit. PythoC ist ein vielversprechendes Projekt, dass verspricht, Devs das Leben leichter zu machen.DC Studio | shutterstock.com Python und C haben mehr gemeinsam, als es auf den ersten Blick scheint. Die Referenzversion des Python-Interpreters ist in C geschrieben und diverse Python-Drittanbieterbibliotheken wrappen C-Code. Es ist außerdem möglich, C-Code aus Python zu generieren. Das läuft im Regelfall über Bibliotheken wie Cython. Diese nutzen typannotierten Python-Code, um C-Erweiterungsmodule zu generieren. Im Gegensatz dazu verfolgt PythoC – ein relativ neues Projekt – einen anderen Ansatz. Es nutzt typannotierten Python-Code, um C-Code programmgesteuert zu generieren. Allerdings hauptsächlich für die Standalone-Nutzung und – im Vergleich zu Cython – mit deutlich mehr Funktionen, um Code zur Kompilierungszeit zu generieren. Die Entwickler hinter PythoC beschreiben ihren Ansatz folgendermaßen: “C-Level-Runtime, Python-getriebene Compile Time”. Zwar steckt PythoC derzeit (Stand Dezember 2025) noch in den Kinderschuhen – dennoch bringt es bereits ausreichend funktionierende Features an den Start, die zumindest rechtfertigen, einen intensiven Blick auf das Projekt zu werfen. Ein simples PythoC-Programm Der folgende Code beschreibt ein einfaches Programm, das wir aus den PythoC-Beispielen adaptiert haben: from pythoc import compile, i32 @compile def add(x: i32, y: i32) -> i32: return x + y if __name__ == "__main__": print(add(10, 20)) Um anzugeben, welche Funktionen innerhalb eines Moduls zu C kompiliert werden sollen, kommt der @compile-Dekorator von PythoC zum Einsatz, der Type Hints für das Ergebnis und jeden Parameter zur Verfügung stellt. Zu beachten ist, dass auch der PythoC-eigene Hint i32 importiert werden muss (statt den nativen Python-Hint int zu verwenden). Es kommen also maschinennative Ganzzahlen zum Einsatz, nicht die aus Python bekannten Ints mit beliebiger Größe. Wenn Sie das obenstehende Programm ausführen, erhalten Sie nach einer Verzögerung den Output 30. Die zeitliche Verzögerung kommt dadurch zustande, dass der C-Code bei jeder Ausführung “on the fly” kompiliert wird. Über einen Mechanismus, um kompilierten Code wiederzuverwenden (wie es bei Cython der Fall ist), verfügt PythoC noch nicht. Das scheint auf den ersten Blick eine große Einschränkung. Aber genau das ist der springende Punkt: So können Sie PythoC als Code-Generierungssystem für C-Programme nutzen, die unabhängig laufen (anstatt auf C-Module zu setzen, die in Python importiert werden). Mit PythoC Standalone-C-Programme aufsetzen Der folgende Code ist eine neue Version desselben Programms mit unterschiedlichen Verhaltensweisen. from pythoc import compile, i32, ptr, i8 from pythoc.libc.stdio import printf @compile def add(x: i32, y: i32) -> i32: return x + y @compile def main(argc: i32, argv: ptr[ptr[i8]]) -> i32: printf("%u\n", add(10, 20)) if __name__ == "__main__": from pythoc import compile_to_executable compile_to_executable() Am auffälligsten ist in diesem Snippet der unterste Block. Die compile_to_executable()-Funktion macht genau das, was ihr Name vermuten lässt. Wird sie aufgerufen, wird das aktuelle Modul zu einer Executable mit dem gleichen Namen kompiliert, wobei alle mit @compile dekorierten Funktionen enthalten sind. Ein weiterer Unterschied zum ersten Code-Beispiel: Die main()-Funktion weist nun dieselbe Signatur auf, wie eine entsprechende Funktion in einem C-Programm. Das nutzt das kompilierte Executable automatisch als Einstiegspunkt. Wenn Sie das obenstehende Programm ausführen, wird die generierte ausführbare Datei (die in einem build-Unterverzeichnis zu finden ist) nicht automatisch ausgeführt – das müssen Sie selbst erledigen. Das Ziel hierbei ist es, ein eigenständiges C-Programm zu erstellen, das sich nicht von einem manuell erstellten unterscheidet, dabei jedoch die Python-Syntax verwendet. C-Funktionen erweitern mit PythoC Bis auf wenige Ausnahmen kann PythoC Code generieren, der die Funktionen und die Laufzeitumgebung von C vollumfänglich ausnutzt. Ähnlich wie Typ-Annotationen verwendet werden, um primitive Datentypen anzugeben, kann die ptr[T]-Annotation eingesetzt werden, um einen Pointer zu beschreiben, während array[T, N] für N-dimensionale Arrays vom Typ T zum Einsatz kommt. Structs, Unions und Enums lassen sich erstellen, indem Sie Python-Klassen dekorieren – wobei alle üblichen Operatoren und Kontrollflussoperationen (mit Ausnahme von goto) funktionieren. Anstelle von switch/case verwenden Sie einfach match/case, allerdings sind Fall-Through-Fälle nicht verfügbar. Eine weitere fehlende Funktion: Arrays mit variabler Länge. In C wird diese Funktion nur in C11 und höher unterstützt – wobei Compiler-Support optional ist. Es ist deshalb nicht verwunderlich, dass PythoC dafür noch keinen Support bietet. Mit PythoC Code zur Kompilierungszeit generieren Es ist möglich, Cython für die Code-Generierung zur Kompilierungszeit zu verwenden. Soll heißen: Sie können verschiedene Arten von C-Code erstellen oder sogar auf Python-Code zurückgreifen, je nachdem, was zur Kompilierungszeit geschieht. PythoC kann diesbezüglich mit Fähigkeiten aufwarten, die Cython nicht bietet. Ein Beispiel aus der PythoC-Dokumentation: from pythoc import compile, struct, i32, f64 def make_point(T): @struct(suffix=T) class Point: x: T y: T @compile(suffix=T) def add_points(p1: Point, p2: Point) -> Point: result: Point = Point() result.x = p1.x + p2.x result.y = p1.y + p2.y return result return Point, add_points Point_i32, add_i32 = make_point(i32) Point_f64, add_f64 = make_point(f64) Die make_point(T)-Funktion nimmt Typ-Annotationen (i32, f64) entgegen und generiert zur Kompilierungszeit typspezifische Versionen der Point-Klasse und der add_points-Funktionen. Der suffix-Parameter für @compile bedeutet “verändere den Namen des generierten Objekts so, dass der Typ im Namen verwendet wird”. So wird beispielsweise Point zu Point_i32 beziehungsweise Point_i64. Das ist in C eine Möglichkeit, zwischen mehreren Versionen derselben Funktion mit unterschiedlicher Typsignatur zu unterscheiden. Memory-Safety-Funktionen in PythoC Die Bugs, die dem manuellen Memory Management von C entspringen können, dürften jedem Nutzer der Sprache allzu bekannt sein. Um dieses Problem zu beheben, verfügt Cython über Memory-Safety-Funktionen. PythoC bietet in diesem Zusammenhang allerdings einzigartige, typbasierte Funktionen. Eine davon ist etwa Linear Types. Mit dem linear-Import ist es möglich einen “Proof” zu generieren, der in der Regel mit einer Speicherzuweisung einhergeht und “verbraucht” werden muss, wenn derselbe Speicher wieder freigegeben wird. Wenn dabei nicht für jedes prf=linear() ein passendes consume(prf) vorliegt, generiert der PythoC-Compiler einen Fehler. Die oben verlinkte Dokumentation zeigt, wie sich über einfache lmalloc()/lfree()-Funktionen Speicher sicher zuweisen und freigeben lässt. Zwar gibt es keine Vorschrift, dass lineare Typen anstelle von manuell verwendeten malloc()/free() zum Einsatz kommen müssen. Aber diese können viele manuelle Überprüfungen automatisieren und diese zur Kompilierungszeit statt zur Laufzeit zentralisieren. Eine weitere typbasierte Sicherheitsfunktion bieten Refinement Types. Die Idee dahinter ist, eine Funktion definieren können, die eine bestimmte Art von Überprüfung durchführt – etwa auf einen Null Pointer – mit einem Boolean als Ergebnis. Anschließend können Sie die refine()-Funktion verwenden, um einen Wert an diese Funktion zu übergeben und einen für diese Funktion spezifischen Typ zurückzubekommen – refined[func]. Dadurch kann der Compiler sicherstellen, dass der Typ vor der Rückgabe auf bestimmte Weise behandelt werden muss. Und: Allgemeine Überprüfungen lassen sich an einer einzigen Stelle im Code durchführen. Weil das Typsystem von Cython hauptsächlich dazu dient, das Verhalten von C direkt zu emulieren, enthält dieses kein vergleichbares Feature. Die PythoC-Zukunft PythoC ist noch recht neu, daher ist es relativ offen, wohin sich das Projekt künftig entwickelt. Eine Möglichkeit wäre eine engere Integration mit Python zur Laufzeit. So könnte beispielsweise ein @cached-Dekorator Module künftig einmalig im Voraus kompilieren (statt jedesmal neu) und die kompilierten Module dann wiederverwenden, wenn sie über Python aufgerufen werden. Das würde natürlich auch eine Integration in das bestehende Modul-Build-System von Python erfordern. Möglicherweise ist ein solches Maß an Integration nicht Teil der Projektziele. Aber es würde PythoC für diejenigen, die C und Python integrieren, noch nützlicher machen. (fm) Ingenieur ABONNIERE UNSEREN NEWSLETTER Von unseren Redakteuren direkt in Ihren Posteingang Beginnen Sie, indem Sie unten Ihre E-Mail-Adresse eingeben. Bitte geben Sie eine gültige E-Mail-Adresse ein. Abonnieren