Home Nieuws Schrijf C-code zonder C te leren: de magie van PythoC

Schrijf C-code zonder C te leren: de magie van PythoC

7
0
Schrijf C-code zonder C te leren: de magie van PythoC

laatst een interessante bibliotheek waar ik nog nooit van had gehoord.

PythoC is een Domain-Specific Language (DSL)-compiler waarmee ontwikkelaars C-programma’s kunnen schrijven met behulp van de standaard Python-syntaxis. Het neemt een statisch getypeerde subset van Python-code en compileert deze rechtstreeks naar de oorspronkelijke machinecode via LLVM IR (Low Level Virtual Machine Intermediate Representation).

LLVM IR is een platformonafhankelijk codeformaat dat intern wordt gebruikt door het LLVM-compilerframework. Compilers vertalen de broncode eerst naar LLVM IR, en vervolgens verandert LLVM die IR in geoptimaliseerde machinecode voor specifieke CPU’s (x86, ARM, enz.).

Een kernontwerpfilosofie van PythoC is: C-equivalente runtime + door Python aangedreven compileertijd, en het heeft de volgende bijna unieke verkoopargumenten.

1. Creëert zelfstandige native uitvoerbare bestanden

In tegenstelling tot tools zoals Cython, die voornamelijk worden gebruikt om C-extensies te maken om bestaande Python-scripts te versnellen, kan PythoC volledig onafhankelijke, zelfstandige uitvoerbare bestanden in C-stijl genereren. Eenmaal gecompileerd, heeft het resulterende binaire bestand geen Python-interpreter of een garbage collector nodig om te worden uitgevoerd.

2. Heeft controle op laag niveau met Python-syntaxis

PythoC weerspiegelt de mogelijkheden van C, maar verpakt ze in de schonere syntaxis van Python. Om dit te bereiken gebruikt het machine-native typehints in plaats van de standaard dynamische typen van Python.

  • Primitieven: i32, i8, f64, enz.
  • Geheugenstructuren: Pointers (ptr(T)), arrays (array(T, N)) en structs (gemaakt door standaard Python-klassen te decoreren).
  • Handmatig geheugenbeheer: Omdat het standaard geen garbage collector gebruikt, is het geheugenbeheer expliciet, net als in C. Het biedt echter moderne, optionele veiligheidscontroles, zoals lineaire typen (die ervoor zorgen dat elke toewijzing expliciet wordt ongedaan gemaakt om lekken te voorkomen) en soorten verfijning (om validatiecontroles tijdens het compileren af ​​te dwingen).

Python als metaprogrammeermachine

Een van de krachtigste functies van PythoC is de afhandeling van de compilatiestap. Omdat de compileeromgeving alleen maar Python is, kunt u standaard Python-logica gebruiken om uw PythoC-code te genereren, manipuleren en specialiseren voor het wordt gecompileerd tot LLVM. Dit geeft u zeer flexibele mogelijkheden voor het genereren van code tijdens het compileren (vergelijkbaar met C++-sjablonen, maar aangestuurd door pure Python).

Het klinkt veelbelovend, maar voldoet de realiteit aan de hype? Oké, laten we deze bibliotheek in actie zien. Het installeren ervan is eenvoudig, zoals bij de meeste Python-bibliotheken is het slechts een pip-installatie zoals deze:

pip install pythoc

Maar het is waarschijnlijk beter om een ​​goede ontwikkelomgeving op te zetten waarin u uw verschillende projecten in silo’s kunt opslaan. In mijn voorbeeld gebruik ik het UV-hulpprogramma, maar gebruik de methode waarmee u zich het prettigst voelt. Typ de volgende opdrachten in uw opdrachtregelterminal.

C:Usersthomaprojects> cd projects
C:Usersthomaprojects> uv init pythoc_test
C:Usersthomaprojects> cd pythoc_test
C:Usersthomaprojectspythoc_test> uv venv --python 3.12
C:Usersthomaprojectspythoc_test> .venvScriptsactivate
(pythoc_test) C:Usersthomaprojectspythoc_test> uv pip install pythoc

Een eenvoudig voorbeeld

Om PythoC te gebruiken, definieert u functies met behulp van specifieke machinetypen en markeert u deze met de compilatie van PythoC decorateur. Er zijn twee manieren om uw PythoC-code uit te voeren. Je kunt de gecompileerde bibliotheek op deze manier rechtstreeks vanuit Python aanroepen:

from pythoc import compile, i32

@compile
def add(x: i32, y: i32) -> i32:
    return x + y

# Can compile to native code
@compile
def main() -> i32:
    return add(10, 20)

# Call the compiled dynamic library from Python directly
result = main()
print(result)

Voer het dan zo uit.

(pythoc_test) C:Usersthomaprojectspythoc_test>python test1.py

30

Of u kunt een zelfstandig uitvoerbaar bestand maken dat u onafhankelijk van Python kunt uitvoeren. Gebruik hiervoor code als deze.

from pythoc import compile, i32

@compile
def add(x: i32, y: i32) -> i32:
    print(x + y)
    return x + y

# Can compile to native code
@compile
def main() -> i32:
    return add(10, 20)

if __name__ == "__main__":
    from pythoc import compile_to_executable
    compile_to_executable()

Wij voeren het op dezelfde manier uit.

(pythoc_test) C:Usersthomaprojectspythoc_test>python test4.py

Successfully compiled to executable: buildtest4.exe
Linked 1 object file(s)

Deze keer zien we geen uitvoer. In plaats daarvan maakt PythoC een build-map onder uw huidige map en maakt daar vervolgens een uitvoerbaar bestand aan dat u kunt uitvoeren.

(pythoc_test) C:Usersthomaprojectspythoc_test>dir buildtest4*
 Volume in drive C is Windows
 Volume Serial Number is EEB4-E9CA

 Directory of C:Usersthomaprojectspythoc_testbuild

26/02/2026  14:32               297 test4.deps
26/02/2026  14:32           168,448 test4.exe
26/02/2026  14:32               633 test4.ll
26/02/2026  14:32               412 test4.o
26/02/2026  14:32                 0 test4.o.lock
26/02/2026  14:32         1,105,920 test4.pdb

We kunnen het bestand test4.exe uitvoeren net zoals elk ander uitvoerbaar bestand.

(pythoc_test) C:Usersthomaprojectspythoc_test>buildtest4.exe

(pythoc_test) C:Usersthomaprojectspythoc_test>

Maar wacht even. In onze Python-code hebben we expliciet gevraagd om het optelresultaat af te drukken, maar we zien geen enkele uitvoer. Wat is er aan de hand?

Het antwoord is dat de ingebouwde functie print() van Python afhankelijk is van de Python-interpreter die op de achtergrond draait om uit te vinden hoe objecten moeten worden weergegeven. Omdat PythoC dat allemaal weghaalt om een ​​klein, razendsnel native uitvoerbaar bestand te bouwen, wordt de print-instructie verwijderd.

Om in een native binair bestand naar het scherm af te drukken, moet u de standaard C-bibliotheekfunctie gebruiken: printf.

Hoe printf te gebruiken in PythoC

In C (en dus in PythoC) zijn voor het afdrukken van variabelen formaatspecificaties vereist. U schrijft een tekenreeks met een tijdelijke aanduiding (zoals %d voor een decimaal geheel getal) en geeft vervolgens de variabele door die u wilt invoegen in die tijdelijke aanduiding.

Hier ziet u hoe u onze code bijwerkt om de C printf-functie te importeren en correct te gebruiken:

from pythoc import compile, i32, ptr, i8, extern

# 1. Tell PythoC to link to the standard C printf function
@extern
def printf(fmt: ptr(i8), *args) -> i32:
    pass

@compile
def add(x: i32, y: i32) -> i32:
  
    printf("Adding 10 and 20 = %dn", x+y)
    return x + y

@compile
def main() -> i32:
    result = add(10, 20)
    
    # 2. Use printf with a C-style format string. 
    # %d is the placeholder for our integer (result).
    # n adds a new line at the end.
   
    
    return 0

if __name__ == "__main__":
    from pythoc import compile_to_executable
    compile_to_executable()

Als we nu de bovenstaande code opnieuw uitvoeren en het resulterende uitvoerbare bestand uitvoeren, wordt onze uitvoer wat we hadden verwacht.

(pythoc_test) C:Usersthomaprojectspythoc_test>python test5.py
Successfully compiled to executable: buildtest5.exe
Linked 1 object file(s)

(pythoc_test) C:Usersthomaprojectspythoc_test>buildtest5.exe
Adding 10 and 20 = 30

Is het echter echt de moeite waard?

Alle dingen waar we het over hebben gehad zullen alleen de moeite waard zijn als we echte snelheidsverbeteringen in onze code zien. Laten we voor ons laatste voorbeeld eens kijken hoe snel onze gecompileerde programma’s kunnen worden vergeleken met het equivalent in Python, en dat zou onze vraag definitief moeten beantwoorden.

Ten eerste de reguliere Python-code. We zullen een recursieve Fibonacci-berekening gebruiken om een ​​langlopend proces te simuleren. Laten we het veertigste Fibonacci-getal berekenen.

import time

def fib(n):
    # This calculates the sequence recursively
    if n 

Ik kreeg dit resultaat toen ik de bovenstaande code uitvoerde.

(pythoc_test) C:Usersthomaprojectspythoc_test>python test6.py
Starting Standard Python speed test...
Result: 102334155
Time taken: 15.1611 seconds

Nu voor de op PythoC gebaseerde code. Nogmaals, net als bij de print-instructie in ons eerdere voorbeeld, kunnen we niet alleen de reguliere importtimingrichtlijn van Python gebruiken voor onze timings. In plaats daarvan moeten we de standaard timingfunctie rechtstreeks van de programmeertaal C lenen: klok(). We definiëren dit op dezelfde manier als de printf-instructie die we eerder gebruikten.

Hier is het bijgewerkte PythoC-script met de ingebouwde C-timer.

from pythoc import compile, i32, ptr, i8, extern

# 1. Import C's printf
@extern
def printf(fmt: ptr(i8), *args) -> i32:
    pass

# 2. Import C's clock function
@extern
def clock() -> i32:
    pass

@compile
def fib(n: i32) -> i32:
    if n  i32:
    printf("Starting PythoC speed test...n")
    
    # Get the start time (this counts in "ticks")
    start_time = clock()
    
    # Run the heavy calculation
    result = fib(40)
    
    # Get the end time
    end_time = clock()
    
    # Calculate the difference. 
    # Note: On Windows, 1 clock tick = 1 millisecond.
    elapsed_ms = end_time - start_time
    
    printf("Result: %dn", result)
    printf("Time taken: %d millisecondsn", elapsed_ms)
    
    return 0

if __name__ == "__main__":
    from pythoc import compile_to_executable
    compile_to_executable()

Mijn output deze keer was:

(pythoc_test) C:Usersthomaprojectspythoc_test>python test7.py
Successfully compiled to executable: buildtest7.exe
Linked 1 object file(s)

(pythoc_test) C:Usersthomaprojectspythoc_test>buildtest7.exe
Starting PythoC speed test...
Result: 102334155
Time taken: 308 milliseconds

En in dit kleine voorbeeld zien we, hoewel de code iets complexer is, het echte voordeel van het gebruik van gecompileerde talen zoals C. Ons uitvoerbare bestand was maar liefst 40x sneller dan de equivalente Python-code. Niet te armoedig.

Voor wie is PythoC bedoeld?

Ik zie drie hoofdtypen gebruikers voor PythoC.

1/ Zoals we in onze Fibonacci-snelheidstest hebben gezien, kan standaard Python traag zijn bij zwaar wiskundig werk. PythoC kan nuttig zijn voor elke Python-ontwikkelaar die natuurkundige simulaties, complexe algoritmen of aangepaste dataverwerkingspijplijnen bouwt en tegen een prestatiemuur aanloopt.

2/ Programmeurs die nauw samenwerken met computerhardware (zoals het bouwen van game-engines, het schrijven van stuurprogramma’s of het programmeren van kleine IoT-apparaten) schrijven meestal in C omdat ze het computergeheugen handmatig moeten beheren.

PythoC zou deze ontwikkelaars kunnen aanspreken omdat het dezelfde handmatige geheugencontrole biedt (met behulp van pointers en native types), maar het hen in staat stelt Python te gebruiken als een “metaprogrammeer”-engine om schonere, flexibelere code te schrijven voordat deze tot op hardwareniveau wordt gecompileerd.

3/ Als u een nuttig Python-script schrijft en dit met een collega wilt delen, moet die collega meestal Python installeren, een virtuele omgeving opzetten en uw afhankelijkheden downloaden. Het kan een gedoe zijn, vooral als de beoogde gebruiker niet erg IT-geletterd is. Met PythoC kan iedereen, zodra je het gecompileerde C-programma hebt, het uitvoeren door gewoon op het bestand te dubbelklikken.

En voor wie het niet is

De keerzijde van het bovenstaande is dat PythoC waarschijnlijk niet de beste tool is voor een webontwikkelaar, aangezien prestatieknelpunten meestal netwerk- of databasesnelheden zijn, en niet CPU-berekeningssnelheden.

Evenzo, als u al gebruiker bent van geoptimaliseerde bibliotheken zoals NumPy, zult u ook niet veel voordelen zien.

Samenvatting

In dit artikel maakte u kennis met de relatief nieuwe en onbekende PythoC-bibliotheek. Hiermee kun je Python gebruiken om supersnelle stand-alone uitvoerbare C-code te maken.

Ik gaf verschillende voorbeelden van het gebruik van Python en de PythoC-bibliotheek om uitvoerbare C-programma’s te produceren, waaronder een die een ongelooflijke snelheid liet zien bij het uitvoeren van het uitvoerbare bestand geproduceerd door de PythoC-bibliotheek vergeleken met een standaard Python-programma.

Een probleem dat je zult tegenkomen is dat Python-imports niet worden ondersteund in PythoC-programma’s, maar ik heb ook laten zien hoe je dit kunt omzeilen door ze te vervangen door gelijkwaardige ingebouwde C-programma’s.

Ten slotte besprak ik wie volgens mij het soort Python-programmeurs waren die een voordeel zouden zien in het gebruik van PythonC in hun werklasten, en degenen die dat niet zouden doen.

Ik hoop dat dit je zin heeft gewekt om te zien voor welke soorten gebruik je PythoC kunt gebruiken. Je kunt veel meer over deze nuttige bibliotheek leren door de GitHub-repository te bekijken via de volgende link.

https://github.com/1flei/PythoC

Nieuwsbron

LAAT EEN REACTIE ACHTER

Vul alstublieft uw commentaar in!
Vul hier uw naam in