Warum braucht Python keinen Compiler?

29

Ich frage mich nur (jetzt, wo ich mit C ++ angefangen habe, das einen Compiler benötigt), warum Python keinen Compiler benötigt.

Ich gebe einfach den Code ein, speichere ihn als Exec und führe ihn aus. In C ++ muss ich Builds und all das andere lustige Zeug machen.

Billjk
quelle
4
Python ist nur eine Sprache mit vielen Implementierungen. Iron Python wird auf die gleiche Weise wie C # und C ++ kompiliert, und es kann auch andere ähnliche Implementierungen geben.
Job
1
C # und C ++ werden nicht auf die gleiche Weise kompiliert - obwohl Sie argumentieren könnten, dass beide letztendlich als Maschinenanweisungen enden, aber wenn Sie dies tun, können Sie sagen, dass BASIC auch auf die gleiche Weise kompiliert wird.
Gbjbaanb
7
@gbjbaanb, aber andererseits wird Englisch nicht kompiliert, und die semantische Analyse eines Satzes kann zu zwei gleichwertigen Ergebnissen führen. Das oben Gesagte könnte lauten: "Eisenpython wird kompiliert, genau wie C # und C ++ kompiliert"
Rune FS
Welche Plattform / Software verwenden Sie, um Ihren Python-Code zu schreiben? Wenn Sie eine .py-Datei schreiben, handelt es sich nicht um eine ausführbare Datei. Es ist immer noch eine Quellcode-Datei. Von der Befehlszeile aus pythoninterpretieren Sie die .py-Datei mit dem Befehl, oder wenn Sie IDLE oder Eclipse verwenden, erledigt die IDE dies für Sie.
Rick Henderson

Antworten:

68

Python hat einen Compiler! Man merkt es einfach nicht, weil es automatisch läuft. Sie können jedoch feststellen, dass es vorhanden ist: Sehen Sie sich die Dateien an .pyc(oder .pyoprüfen Sie, ob das Optimierungsprogramm aktiviert ist), die für die von Ihnen erstellten Module erstellt wurden import.

Außerdem wird es nicht mit dem Code des nativen Computers kompiliert. Stattdessen wird ein Bytecode kompiliert, der von einer virtuellen Maschine verwendet wird. Die virtuelle Maschine selbst ist ein kompiliertes Programm. Dies ist der Funktionsweise von Java sehr ähnlich. In der Tat so ähnlich, dass es eine Python-Variante ( Jython ) gibt, die stattdessen mit dem Byte-Code der Java Virtual Machine kompiliert wird! Es gibt auch IronPython , das mit Microsofts CLR (von .NET verwendet) kompiliert wird. (Der normale Python-Bytecode-Compiler wird manchmal als CPython bezeichnet, um ihn von diesen Alternativen zu unterscheiden.)

C ++ muss seinen Kompilierungsprozess offenlegen, da die Sprache selbst unvollständig ist. Es gibt weder alles an, was der Linker wissen muss, um Ihr Programm zu erstellen, noch können Kompilierungsoptionen portabel angegeben werden (einige Compiler lassen dies zu #pragma, aber das ist kein Standard). Also musst du den Rest der Arbeit mit Makefiles und möglicherweise Auto Hell (autoconf / automake / libtool) machen. Dies ist wirklich nur ein Überbleibsel von dem, was C getan hat. Und C hat es so gemacht, weil es den Compiler einfach gemacht hat, was ein Hauptgrund dafür ist, dass er so beliebt ist (jeder konnte in den 80ern einen einfachen C-Compiler entwickeln).


Einige Dinge, die sich auf den Compiler- oder Linker-Betrieb auswirken können, jedoch nicht in der C- oder C ++ - Syntax angegeben sind:

  • Abhängigkeitsauflösung
  • Anforderungen an externe Bibliotheken (einschließlich Abhängigkeitsreihenfolge)
  • Optimierungsstufe
  • Warnungseinstellungen
  • Sprachspezifikation Version
  • Linkerzuordnungen (welcher Abschnitt wohin im endgültigen Programm führt)
  • Zielarchitektur

Einige davon können erkannt, aber nicht angegeben werden. Ich kann z. B. feststellen, welches C ++ verwendet wird __cplusplus, aber ich kann nicht angeben, dass C ++ 98 für meinen Code innerhalb des Codes selbst verwendet wird. Ich muss es als Flag an den Compiler im Makefile übergeben oder eine Einstellung in einem Dialog vornehmen.

Während Sie vielleicht denken, dass im Compiler ein "Abhängigkeitsauflösungssystem" existiert, das automatisch Abhängigkeitsdatensätze generiert, geben diese Datensätze nur an, welche Header-Dateien eine bestimmte Quelldatei verwendet. Sie können nicht angeben, welche zusätzlichen Quellcodemodule zum Verknüpfen mit einem ausführbaren Programm erforderlich sind, da es in C oder C ++ keine Standardmethode gibt, die angibt, dass eine bestimmte Headerdatei die Schnittstellendefinition für ein anderes Quellcodemodul ist, und nicht nur eine Reihe von Zeilen, die an mehreren Stellen angezeigt werden sollen, damit Sie sich nicht wiederholen. Es gibt Traditionen in Dateinamenskonventionen, aber diese sind dem Compiler und Linker nicht bekannt oder werden von ihm nicht erzwungen.

Einige davon können mit eingestellt werden #pragma, aber dies ist kein Standard, und ich habe vom Standard gesprochen. All diese Dinge könnten durch eine Norm spezifiziert werden, waren jedoch nicht im Interesse der Abwärtskompatibilität. Die vorherrschende Weisheit ist, dass Makefiles und IDEs nicht kaputt sind, also reparieren Sie sie nicht.

Python erledigt dies alles in der Sprache. Gibt beispielsweise importeine explizite Modulabhängigkeit an, impliziert den Abhängigkeitsbaum und Module werden nicht in Header- und Quelldateien (dh Schnittstelle und Implementierung) aufgeteilt.

Mike DeSimone
quelle
3
Die C-Implementierung von Python ist CPython , Cython ist etwas anderes.
Greg Hewgill
4
Andere Gründe, warum C zu Maschinencode kompiliert wurde, waren, dass es nicht viel mehr als ein verherrlichter Assembler sein sollte, weil Bytecode-Interpreter auf der Hardware, die sie hatten, technisch nicht ausführbar waren, und weil eine der wichtigsten Aufgaben das Schreiben eines Betriebssystemkerns war.
Tdammers
2
@BillyONeal mit der einzigen großen Ausnahme, dass Sie als Programmierer in c / c ++ bestimmte Dinge in Python erledigen müssen (entweder Makefiles oder alles in denselben Blob). Sie erledigen Ihre Arbeit und den Compiler einfach zusammen mit der VM kümmert sich um den Rest
Rune FS
3
"C ++ muss seinen Kompilierungsprozess offenlegen, weil die Sprache selbst unvollständig ist" Äh, was?
Leichtigkeit Rennen mit Monica
3
Du hast den Teil gleich danach gelesen , oder? "Es gibt weder alles an, was der Linker wissen muss, um Ihr Programm zu erstellen, noch können Kompilieroptionen portabel angegeben werden." Sie können nicht einfach eine C ++ - Datei erstellen, indem Sie sie einem Compiler zuführen. Häufig müssen Sie Metadaten wie Kompilierungsflags, Include-Pfade usw. bereitstellen. Diese Metadaten werden vom Standard nicht angegeben und sind nicht portierbar. Aus diesem Grund müssen wir andere Elemente wie make, cmake, Visual Studio oder was auch immer in diese ziehen Beende die Aufgabe. Der Standard muss also einige Dinge wie in der Kompilierungseinheit und andere als programmweit aufrufen.
Mike DeSimone
7

Python ist eine interpretierte Sprache. Dies bedeutet, dass sich auf Ihrem Computer Software befindet, die den Python-Code liest und die "Anweisungen" an den Computer sendet. Der Wikipedia-Artikel zu interpretierten Sprachen könnte von Interesse sein.

Wenn eine Sprache wie C ++ (eine kompilierte Sprache) kompiliert wird, bedeutet dies, dass sie in Maschinencode konvertiert wird, der bei der Ausführung direkt von der Hardware gelesen wird. Der Wikipedia - Artikel über kompilierten Sprachen könnte einen interessanten Kontrast.

Zenon
quelle
21
Es gibt keine interpretierte oder kompilierte Sprache. Eine Sprache ist ein abstrakter Satz mathematischer Regeln. Eine Sprache wird nicht kompiliert oder interpretiert. Eine Sprache einfach ist . Zusammenstellung und Interpretation sind Eigenschaften des Compilers oder Interpreten (duh!), Nicht der Sprache. Jede Sprache kann mit einem Compiler implementiert werden und jede Sprache kann mit einem Interpreter implementiert werden. Die meisten Sprachen haben sowohl kompilierte als auch interpretierte Implementierungen. Es gibt Interpreter für C ++ und Compiler für Python. (Tatsächlich haben alle derzeit vorhandenen Python-Implementierungen Compiler.)
Jörg W Mittag
4
Die meisten modernen Hochleistungs-Sprachimplementierungen kombinieren sowohl einen Interpreter als auch einen Compiler (oder sogar mehrere Compiler), um eine maximale Leistung zu erzielen. Tatsächlich ist es unmöglich , ein Programm ohne einen Interpreter auszuführen . Ein Compiler ist schließlich nur ein Programm, das ein Programm von einer Sprache in eine andere Sprache übersetzt. Aber an einem gewissen Punkt muss man eigentlich läuft das Programm, das von einem Dolmetscher durchgeführt wird (die können oder auch nicht in Silizium implementiert werden).
Jörg W Mittag
10
@ JörgWMittag: Du hast technisch recht. Die meisten Sprachen wurden jedoch für einen interpretierten Kontext oder für die vollständige Kompilierung entwickelt. Das Schreiben eines Interpreters für GW BASIC oder Common Lisp ist viel einfacher als beispielsweise für C ++ oder C #. Python verliert viele seiner Verkaufsargumente ohne die interaktive Umgebung. Einen Compiler für PHP zu schreiben ist verdammt schwer und wahrscheinlich schrecklich ineffizient, da die kompilierte ausführbare Datei aufgrund von eval () und ähnlichen Konstrukten den gesamten PHP-Interpreter enthalten müsste - man könnte argumentieren, dass ein solcher Compiler schummeln würde.
Tdammers
2
@tdammers, ja. Wir können vernünftigerweise "kompilierte Sprache" verwenden, um "normalerweise kompilierte Sprache" zu bedeuten. Aber das übersieht den Punkt, dass PHP, Java, Python, Lua und C # alle als Compiler für Bytecode implementiert sind. Für alle diese Sprachen wurden auch JITs implementiert. Daher kann man einige dieser Sprachen nicht als kompiliert oder interpretiert bezeichnen, da sie dieselbe Implementierungsstrategie haben.
Winston Ewert
2
@ BillyONeal, nicht wahr, zumindest für Python. Sie können Python-Bytecode verteilen und diesen ohne die Quelle ausführen. Aber es stimmt, dass Sie Python nicht ohne einen Compiler verteilen können.
Winston Ewert
5

Nicht alle kompilierten Sprachen verfügen über einen direkten Bearbeitungszyklus zum Kompilieren von Links.

Worauf Sie stoßen, ist eine Funktion / Einschränkung von C ++ (oder zumindest von C ++ - Implementierungen).

Um etwas zu tun, müssen Sie Ihren Code in Dateien speichern und ein monolithisches Image durch einen Prozess namens Verknüpfen erstellen.

Insbesondere ist es dieser monolithische Verknüpfungsprozess, der mit der Unterscheidung zwischen Kompilieren und Interpretieren verwechselt wird.

Einige Sprachen erledigen all diese Dinge viel dynamischer, indem sie den unbeholfenen monolithischen Verknüpfungsschritt eliminieren und nicht das Kompilieren in Maschinencode. Der Quellcode wird weiterhin zu Objektdateien kompiliert, diese werden jedoch in ein Laufzeitabbild geladen, anstatt in eine monolithische ausführbare Datei verknüpft zu werden.

Sie sagen "reload this module", und es lädt die Quelle und interpretiert sie oder kompiliert sie, abhängig von einem Modusschalter.

Die Linux-Kernel-Programmierung weist einige dieser Merkmale auf, obwohl Sie in C arbeiten. Sie können ein Modul neu kompilieren und laden und entladen. Natürlich ist Ihnen immer noch klar, dass Sie eine ausführbare Datei produzieren, und diese wird von einem komplexen Build-System verwaltet, das noch einige manuelle Schritte enthält. Fakt ist jedoch, dass Sie letztendlich nur dieses kleine Modul entladen und neu laden können und nicht den gesamten Kernel neu starten müssen.

Einige Sprachen weisen eine noch feinkörnigere Modularisierung auf, und das Erstellen und Laden erfolgt innerhalb ihrer Laufzeit, sodass es nahtloser ist.

Kaz
quelle
2

Was für eine Ablenkung von der ursprünglichen Frage ... Ein Punkt, der nicht erwähnt wird, ist, dass die Quelle eines Python-Programms das ist, was Sie verwenden und verbreiten. Aus Sicht des Benutzers ist es das Programm. Wir tendieren dazu, Dinge in Kategorien zu vereinfachen, die nicht gut definiert sind.

Kompilierte Programme werden normalerweise als eigenständige Dateien mit Maschinencode betrachtet. (Zugegebenermaßen häufig mit Links zu dynamischen Linkbibliotheken, die mit bestimmten Betriebssystemen verknüpft sind). Dies sagte ... es gibt Variationen der meisten Programmiersprachen, die als kompiliert oder interpretiert beschrieben werden könnten.

Python benötigt keinen Compiler, da es sich auf eine Anwendung (einen so genannten Interpreter) stützt, die den Code kompiliert und ausführt, ohne den erstellten Maschinencode in einer Form zu speichern, auf die Sie problemlos zugreifen oder die Sie verteilen können.

Stephen Robinson
quelle
1

Alle Programmiersprachen erfordern die Übersetzung von menschlichen Konzepten in einen Zielmaschinencode. Sogar die Assemblersprache muss in Maschinencode übersetzt werden. Diese Übersetzung findet normalerweise in den folgenden Phasen statt:

Phase 1: Analyse und Übersetzung (Parsing) in einen Zwischencode. Phase 2: Übersetzung des Zwischencodes in Zielmaschinencode mit Platzhaltern für externe Referenzen. Phase 3: Auflösung der externen Referenzen und Verpackung in ein maschinenausführbares Programm.

Diese Übersetzung wird häufig als Vorkompilierung und "Just in Time" (JIT) oder Laufzeitkompilierung bezeichnet.

Sprachen wie C, C ++, COBOL, Fortran, Pascal (nicht alle) und Assembly sind vorkompilierte Sprachen, die direkt vom Betriebssystem ausgeführt werden können, ohne dass ein Interpreter erforderlich ist.

Sprachen wie Java, BASIC, C # und Python werden interpretiert. Sie alle verwenden den in Phase 1 erstellten Zwischencode, unterscheiden sich jedoch manchmal darin, wie sie ihn in Maschinencode umsetzen. Die einfachsten Formulare verwenden diesen Zwischencode, um Maschinencoderoutinen auszuführen, die die erwartete Arbeit ausführen. Andere kompilieren den Zwischencode bis zum Maschinencode und führen die Korrektur externer Abhängigkeiten zur Laufzeit durch. Einmal kompiliert, kann es sofort ausgeführt werden. Außerdem wird der Maschinencode in einem Cache aus zuvor kompiliertem wiederverwendbarem Maschinencode gespeichert, der später wiederverwendet werden kann, wenn die Funktion später erneut benötigt wird. Wenn eine Funktion bereits zwischengespeichert wurde, muss sie der Interpreter nicht erneut kompilieren.

Die meisten modernen Hochsprachen fallen in die Kategorie der gedolmetschten Sprachen (mit JIT). Meist sind es die älteren Sprachen wie C & C ++, die vorkompiliert werden.

Mark Baker
quelle