Ist es möglich, Python zu Maschinencode zu kompilieren?

127

Wie machbar wäre es, Python (möglicherweise über eine C-Zwischendarstellung) in Maschinencode zu kompilieren?

Vermutlich müsste eine Verknüpfung zu einer Python-Laufzeitbibliothek hergestellt werden, und alle Teile der Python-Standardbibliothek, die selbst Python waren, müssten ebenfalls kompiliert (und eingebunden) werden.

Außerdem müssten Sie den Python-Interpreter bündeln, wenn Sie Ausdrücke dynamisch auswerten möchten, aber möglicherweise wäre eine Teilmenge von Python, die dies nicht zulässt, dennoch nützlich.

Würde es Vorteile hinsichtlich Geschwindigkeit und / oder Speichernutzung bieten? Vermutlich würde die Startzeit des Python-Interpreters entfallen (obwohl gemeinsam genutzte Bibliotheken beim Start noch geladen werden müssten).

Andy Bileam
quelle
2
Übrigens wäre Ihre Frage meiner Meinung nach klarer, wenn Sie eher nach "Maschinencode" als nach Objektcode fragen würden.
Torsten Marek

Antworten:

31

Probieren Sie den ShedSkin Python-to-C ++ - Compiler aus, aber er ist alles andere als perfekt. Es gibt auch Psyco - Python JIT, wenn nur eine Beschleunigung erforderlich ist. Aber meiner Meinung nach ist das die Mühe nicht wert. Für geschwindigkeitskritische Teile des Codes besteht die beste Lösung darin, sie als C / C ++ - Erweiterungen zu schreiben.

cleg
quelle
5
Zu Ihrer Information, ShedSkin hat die Windows-Unterstützung eingestellt.
Sorin
2
@ Sorin: Nun, heute unterstützt es Windows ... code.google.com/p/shedskin/downloads/…
2
Die beste Lösung könnte immer noch PyPy sein .
Cees Timmerman
Shedskin hat seit etwa zwei Jahren keine Arbeit mehr daran gemacht. :(
Perkins
53

Wie @ Greg Hewgill sagt, gibt es gute Gründe, warum dies nicht immer möglich ist. Bestimmte Arten von Code (wie sehr algorithmischer Code) können jedoch in "echten" Maschinencode umgewandelt werden.

Es gibt mehrere Möglichkeiten:

  • Verwenden Sie Psyco , das Maschinencode dynamisch ausgibt. Sie sollten jedoch sorgfältig auswählen, welche Methoden / Funktionen konvertiert werden sollen.
  • Verwenden Sie Cython , eine Python- ähnliche Sprache, die in eine Python C-Erweiterung kompiliert wird
  • Verwenden Sie PyPy mit einem Übersetzer von RPython (einer eingeschränkten Teilmenge von Python, die einige der "dynamischsten" Funktionen von Python nicht unterstützt) zu C oder LLVM.
    • PyPy ist immer noch sehr experimentell
    • Nicht alle Erweiterungen sind vorhanden

Danach können Sie eines der vorhandenen Pakete (Freeze, Py2exe, PyInstaller) verwenden, um alles in eine Binärdatei zu packen.

Alles in allem gibt es keine allgemeine Antwort auf Ihre Frage. Wenn Sie leistungskritischen Python-Code haben, versuchen Sie, so viele integrierte Funktionen wie möglich zu verwenden (oder stellen Sie die Frage "Wie mache ich meinen Python-Code schneller?"). Wenn dies nicht hilft, versuchen Sie, den Code zu identifizieren und nach C (oder Cython) zu portieren, und verwenden Sie die Erweiterung.

Torsten Marek
quelle
3
Pypy ist der Nachfolger von Psyco
bcattle
19

py2c ( https://github.com/pradyun/Py2C ) kann Python-Code in c / c ++ konvertieren. Ich bin der Solo-Entwickler von py2c.

Ramchandra Apte
quelle
Dies scheint ein nützliches Werkzeug zu sein. Wird es noch gewartet?
Anderson Green
@AndersonGreen Das letzte Mal, als ich daran gearbeitet habe, befindet es sich in einem frühen Entwicklungsstadium (wahrscheinlich jetzt ähnlich). Ich habe das Projekt verlassen, weil ich faul bin. Wenn Sie den Text "Wichtig" nicht bemerkt haben, wurde er jetzt auf GitHub verschoben.
Ramchandra Apte
Der Link verweist auf ein nicht besiegtes Installationsprogramm , bei dem es sich anscheinend um ein anderes Projekt handelt. Ist py2c noch auf GitHub verfügbar?
Anderson Green
@AndersonGreen Wow das war so lange unbemerkt! Hier gehts.
Ramchandra Apte
Der Link auf code.google.com/p/py2c verweist weiterhin auf das nicht besiegte Installationsprogramm, daher muss es jetzt aktualisiert werden.
Anderson Green
15

PyPy ist ein Projekt zur Neuimplementierung von Python in Python, bei dem die Kompilierung zu nativem Code als eine der Implementierungsstrategien verwendet wird (andere sind VMs mit JIT, JVM usw.). Ihre kompilierten C-Versionen laufen im Durchschnitt langsamer als CPython, bei einigen Programmen jedoch viel schneller.

Shedskin ist ein experimenteller Python-zu-C ++ - Compiler.

Pyrex ist eine Sprache, die speziell zum Schreiben von Python-Erweiterungsmodulen entwickelt wurde. Es wurde entwickelt, um die Lücke zwischen der schönen, benutzerfreundlichen Welt von Python auf hoher Ebene und der chaotischen Welt von C auf niedriger Ebene zu schließen.

pdc
quelle
3
Cython ist die am weitesten verbreitete, aktiv entwickelte freundliche Gabel von Pyrex.
Mike Graham
"Die nette, benutzerfreundliche Welt von Python auf hoher Ebene und die chaotische Welt von C auf niedriger Ebene" - lustig Ich dachte nur, wie C und Assembler "nett" und einfach sind und Python in der " chaotisch "," hochrangig "Welt
Reversed Engineer
14

Nuitka ist ein Python-zu-C ++ - Compiler, der mit libpython verknüpft ist. Es scheint ein relativ neues Projekt zu sein. Der Autor behauptet eine Geschwindigkeitsverbesserung gegenüber CPython im Pystone-Benchmark.

Vieh
quelle
10

Dies mag auf den ersten Blick vernünftig erscheinen, es gibt jedoch viele gewöhnliche Dinge in Python, die nicht direkt auf eine C-Darstellung abgebildet werden können, ohne einen Großteil der Python-Laufzeitunterstützung zu übernehmen. Zum Beispiel fällt mir das Tippen von Enten ein. Viele Funktionen in Python, die Eingaben lesen, können eine Datei oder eine Datei enthalten Objekt , sofern sie bestimmte Operationen unterstützen, z. read () oder readline (). Wenn Sie darüber nachdenken, was erforderlich ist, um diese Art der Unterstützung C zuzuordnen, stellen Sie sich genau vor, was das Python-Laufzeitsystem bereits tut.

Es gibt Dienstprogramme wie py2exe , die ein Python-Programm und die Laufzeit in einer einzigen ausführbaren Datei bündeln (soweit möglich).

Greg Hewgill
quelle
1
Was wäre, wenn mein Ziel darin bestehen würde, sicherzustellen, dass der Code kompiliert wird, da statisch kompilierte Sprachen (zumindest meiner Meinung nach) zur Laufzeit weniger wahrscheinlich explodieren? Ist es möglich, festzustellen , dass einiger foo.xAusdruck wird nicht funktionieren , weil foonicht haben xzu der Zeit sie aufgerufen wird. Gibt es statische Codeprüfer für Python? Python kann zu einer .Net-Assembly kompiliert werden ...
Hamish Grubijan
10

Pyrex ist eine Teilmenge der Python-Sprache, die zu C kompiliert wird und von dem Typ erstellt wurde, der zuerst Listenverständnisse für Python erstellt hat. Es wurde hauptsächlich für das Erstellen von Wrappern entwickelt, kann jedoch in einem allgemeineren Kontext verwendet werden. Cython ist eine aktivere Pyrexgabel.

ConcernedOfTunbridgeWells
quelle
2
Cython ist die am weitesten verbreitete, aktiv entwickelte freundliche Gabel von Pyrex.
Mike Graham
3

Jython verfügt über einen Compiler, der auf JVM-Bytecode abzielt. Der Bytecode ist voll dynamisch, genau wie die Python-Sprache selbst! Sehr cool. (Ja, wie Greg Hewgills Antwort anspielt, verwendet der Bytecode die Jython-Laufzeit, und daher muss die Jython-JAR-Datei mit Ihrer App verteilt werden.)

Chris Jester-Young
quelle
2

Psyco ist eine Art Just-in-Time-Compiler (JIT): Dynamischer Compiler für Python, der Code 2-100-mal schneller ausführt, aber viel Speicher benötigt.

Kurz gesagt: Es führt Ihre vorhandene Python-Software viel schneller aus, ohne dass sich Ihre Quelle ändert, aber es wird nicht wie ein C-Compiler zu Objektcode kompiliert.

Pierre-Jean Coudert
quelle
2

Die Antwort lautet "Ja, das ist möglich". Sie können Python-Code verwenden und versuchen, ihn mithilfe der CPython-API in den entsprechenden C-Code zu kompilieren. Tatsächlich gab es früher ein Python2C-Projekt, das genau das tat, aber ich habe seit vielen Jahren nichts mehr davon gehört (in den 1,5 Tagen von Python habe ich es das letzte Mal gesehen).

Sie können versuchen, den Python-Code so weit wie möglich in natives C zu übersetzen und auf die CPython-API zurückzugreifen, wenn Sie tatsächliche Python-Funktionen benötigen. Ich habe in den letzten ein oder zwei Monaten selbst mit dieser Idee gespielt. Es ist jedoch eine Menge Arbeit, und eine enorme Menge von Python-Funktionen ist sehr schwer in C zu übersetzen: verschachtelte Funktionen, Generatoren, alles andere als einfache Klassen mit einfachen Methoden, alles, was das Ändern von Modulglobalen von außerhalb des Moduls usw. , etc.

Thomas Wouters
quelle
2

Dadurch wird Python nicht zu Maschinencode kompiliert. Ermöglicht jedoch das Erstellen einer gemeinsam genutzten Bibliothek zum Aufrufen von Python-Code.

Wenn Sie nach einer einfachen Möglichkeit suchen, Python-Code von C aus auszuführen, ohne sich auf Execp-Inhalte verlassen zu müssen. Sie können eine gemeinsam genutzte Bibliothek aus Python-Code generieren, der mit einigen Aufrufen der Python-Einbettungs-API umschlossen ist . Nun, die Anwendung ist eine gemeinsam genutzte Bibliothek, eine .so, die Sie in vielen anderen Bibliotheken / Anwendungen verwenden können.

Hier ist ein einfaches Beispiel, das eine gemeinsam genutzte Bibliothek erstellt, die Sie mit einem C-Programm verknüpfen können. Die gemeinsam genutzte Bibliothek führt Python-Code aus.

Die Python-Datei, die ausgeführt wird, lautet pythoncalledfromc.py:

# -*- encoding:utf-8 -*-
# this file must be named "pythoncalledfrom.py"

def main(string):  # args must a string
    print "python is called from c"
    print "string sent by «c» code is:"
    print string
    print "end of «c» code input"
    return 0xc0c4  # return something

Sie können es mit versuchen python2 -c "import pythoncalledfromc; pythoncalledfromc.main('HELLO'). Es wird ausgegeben:

python is called from c
string sent by «c» code is:
HELLO
end of «c» code input

Die gemeinsam genutzte Bibliothek wird wie folgt definiert callpython.h:

#ifndef CALL_PYTHON
#define CALL_PYTHON

void callpython_init(void);
int callpython(char ** arguments);
void callpython_finalize(void);

#endif

Das zugehörige callpython.cist:

// gcc `python2.7-config --ldflags` `python2.7-config --cflags` callpython.c -lpython2.7 -shared -fPIC -o callpython.so

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <python2.7/Python.h>

#include "callpython.h"

#define PYTHON_EXEC_STRING_LENGTH 52
#define PYTHON_EXEC_STRING "import pythoncalledfromc; pythoncalledfromc.main(\"%s\")"


void callpython_init(void) {
     Py_Initialize();
}

int callpython(char ** arguments) {
  int arguments_string_size = (int) strlen(*arguments);
  char * python_script_to_execute = malloc(arguments_string_size + PYTHON_EXEC_STRING_LENGTH);
  PyObject *__main__, *locals;
  PyObject * result = NULL;

  if (python_script_to_execute == NULL)
    return -1;

  __main__ = PyImport_AddModule("__main__");
  if (__main__ == NULL)
    return -1;

  locals = PyModule_GetDict(__main__);

  sprintf(python_script_to_execute, PYTHON_EXEC_STRING, *arguments);
  result = PyRun_String(python_script_to_execute, Py_file_input, locals, locals);
  if(result == NULL)
    return -1;
  return 0;
}

void callpython_finalize(void) {
  Py_Finalize();
}

Sie können es mit dem folgenden Befehl kompilieren:

gcc `python2.7-config --ldflags` `python2.7-config --cflags` callpython.c -lpython2.7 -shared -fPIC -o callpython.so

Erstellen Sie eine Datei mit dem Namen callpythonfromc.c, die Folgendes enthält:

#include "callpython.h"

int main(void) {
  char * example = "HELLO";
  callpython_init();
  callpython(&example);
  callpython_finalize();
  return 0;
}

Kompilieren Sie es und führen Sie Folgendes aus:

gcc callpythonfromc.c callpython.so -o callpythonfromc
PYTHONPATH=`pwd` LD_LIBRARY_PATH=`pwd` ./callpythonfromc

Dies ist ein sehr einfaches Beispiel. Es kann funktionieren, aber je nach Bibliothek kann es immer noch schwierig sein, C-Datenstrukturen nach Python und von Python nach C zu serialisieren. Die Dinge können etwas automatisiert werden ...

Nuitka könnte hilfreich sein.

Es gibt auch Numba, aber beide wollen nicht genau das tun, was Sie wollen. Das Generieren eines C-Headers aus Python-Code ist möglich, jedoch nur, wenn Sie angeben, wie die Python-Typen in C-Typen konvertiert werden sollen, oder auf diese Informationen schließen können. Siehe Python Astroid für einen Python Ast Analyzer.

Amirouche
quelle