Perché Python non ha bisogno di un compilatore?


29

Mi chiedo (ora che ho iniziato con C ++ che ha bisogno di un compilatore) perché Python non ha bisogno di un compilatore?

Ho appena inserito il codice, salvato come un exec ed eseguirlo. In C ++ devo creare build e tutte quelle altre cose divertenti.


4
Python è solo un linguaggio con molte implementazioni. Iron Python viene compilato nello stesso modo in cui vengono compilati C # e C ++ e potrebbero esserci altre implementazioni simili.
Lavoro il

1
C # e C ++ non sono compilati allo stesso modo, anche se si potrebbe obiettare che alla fine finiscono entrambi come istruzioni per la macchina, ma se lo fai allora puoi dire che BASIC è compilato allo stesso modo.
gbjbaanb,

7
@gbjbaanb, ma in questo caso l'inglese non è stato compilato e l'analisi semantica di una frase potrebbe produrre due risultati ugualmente validi e quanto sopra potrebbe essere letto come "il pitone di ferro è compilato esattamente come C # e C ++ è compilato"
Rune FS

Quale piattaforma / software stai usando per scrivere il tuo codice Python? Se si scrive un file .py, non è un eseguibile. È ancora un file di codice sorgente. Dalla riga di comando si utilizza il pythoncomando per interpretare il file .py o se si utilizza IDLE o Eclipse, l'IDE lo fa per sé.
Rick Henderson,

Risposte:


68

Python ha un compilatore! Non te ne accorgi perché funziona automaticamente. Puoi dire che è lì, però: guarda i file .pyc(o .pyose hai l'ottimizzatore attivato) che vengono generati per i moduli che hai import.

Inoltre, non viene compilato nel codice della macchina nativa. Al contrario, si compila in un codice byte utilizzato da una macchina virtuale. La macchina virtuale è essa stessa un programma compilato. Questo è molto simile a come funziona Java; così simile, infatti, che esiste una variante di Python ( Jython ) che si compila invece nel codice byte della Java Virtual Machine! C'è anche IronPython , che si compila in CLR di Microsoft (usato da .NET). (Il normale compilatore di codice byte Python viene talvolta chiamato CPython per chiarire la sua ambiguità da queste alternative.)

Il C ++ deve esporre il suo processo di compilazione perché il linguaggio stesso è incompleto; non specifica tutto ciò che il linker deve sapere per costruire il tuo programma, né può specificare portabilmente le opzioni di compilazione (alcuni compilatori ti permettono di usare #pragma, ma non è standard). Quindi devi fare il resto del lavoro con makefile e possibilmente auto hell (autoconf / automake / libtool). Questo è davvero solo un holdover da come C ha fatto. E C lo ha fatto in questo modo perché ha reso semplice il compilatore, che è uno dei motivi principali per cui è così popolare (chiunque potrebbe creare un semplice compilatore C negli anni '80).


Alcune cose che possono influire sul funzionamento del compilatore o del linker ma non sono specificate nella sintassi di C o C ++:

  • risoluzione delle dipendenze
  • requisiti della libreria esterna (incluso l'ordine di dipendenza)
  • livello di ottimizzazione
  • impostazioni di avviso
  • versione specifica della lingua
  • mappature dei linker (quale sezione va dove nel programma finale)
  • architettura target

Alcuni di questi possono essere rilevati, ma non possono essere specificati; ad esempio, posso rilevare con quale C ++ è in uso __cplusplus, ma non posso specificare che C ++ 98 è quello utilizzato per il mio codice all'interno del codice stesso; Devo passarlo come flag al compilatore nel Makefile o fare un'impostazione in una finestra di dialogo.

Sebbene si possa pensare che esista un sistema di "risoluzione delle dipendenze" nel compilatore, che genera automaticamente i record di dipendenza, questi record indicano solo quali file di intestazione vengono utilizzati da un determinato file di origine. Non possono indicare quali moduli di codice sorgente aggiuntivi sono necessari per il collegamento in un programma eseguibile, poiché non esiste un modo standard in C o C ++ per indicare che un determinato file di intestazione è la definizione dell'interfaccia per un altro modulo di codice sorgente, a differenza di un solo gruppo di linee che desideri mostrare in più punti in modo da non ripetere te stesso. Esistono tradizioni nelle convenzioni di denominazione dei file, ma queste non sono conosciute o applicate dal compilatore e dal linker.

Molti di questi possono essere impostati usando #pragma, ma questo non è standard e stavo parlando dello standard. Tutte queste cose potrebbero essere specificate da uno standard, ma non sono state nell'interesse della retrocompatibilità. La saggezza prevalente è che i makefile e gli IDE non sono rotti, quindi non risolverli.

Python gestisce tutto questo nella lingua. Ad esempio, importspecifica una dipendenza esplicita del modulo, implica l'albero delle dipendenze e i moduli non sono suddivisi in file di intestazione e di origine (ovvero interfaccia e implementazione).


3
L'implementazione in C di Python è CPython , Cython è qualcosa di diverso.
Greg Hewgill,

4
Altre ragioni per cui C compilato per codice macchina erano che era inteso per essere poco più che un assemblatore glorificato, perché gli interpreti bytecode erano tecnicamente irrealizzabili sull'hardware che avevano e perché uno dei compiti più importanti era scrivere un kernel del sistema operativo.
martedì

2
@BillyONeal con l'unica grande eccezione che in c / c ++ tu come programmatore devi fare cose in un certo modo (sia makefile che scaricare ogni cosa nello stesso blob) in Python fai solo il tuo lavoro e il compilatore insieme alla VM si prende cura di tutto il resto
Rune FS

3
"Il C ++ ha bisogno di esporre il suo processo di compilazione perché il linguaggio stesso è incompleto" Ehm, cosa ??
Lightness Races con Monica

3
Hai letto la parte subito dopo , giusto? "non specifica tutto ciò che il linker deve sapere per costruire il tuo programma, né può specificare portabilmente le opzioni di compilazione." Non puoi semplicemente creare qualsiasi file C ++ inviandolo a un compilatore; spesso devi fornire metadati come flag di compilazione, includere percorsi, ecc. Questi metadati non sono specificati dallo standard e non sono portatili, motivo per cui dobbiamo trascinare in altre cose come make, cmake, Visual Studio o qualsiasi altra cosa finisci il lavoro. Quindi lo standard deve richiamare alcune cose come nell'unità di compilazione e altre come a livello di programma.
Mike DeSimone,

7

Python è un linguaggio interpretato. Ciò significa che sul tuo computer è presente un software che legge il codice Python e invia le "istruzioni" alla macchina. L' articolo di Wikipedia sulle lingue interpretate potrebbe essere di interesse.

Quando viene compilato un linguaggio come C ++ (un linguaggio compilato), significa che viene convertito in codice macchina per essere letto direttamente dall'hardware quando eseguito. L' articolo di Wikipedia sui linguaggi compilati potrebbe fornire un contrasto interessante.


21
Non esiste un linguaggio interpretato o compilato. Una lingua è un insieme astratto di regole matematiche. Una lingua non è compilata o interpretata. Una lingua è solo . Compilazione e interpretazione sono tratti del compilatore o dell'interprete (duh!), Non della lingua. Ogni lingua può essere implementata con un compilatore e ogni lingua può essere implementata con un interprete. La maggior parte delle lingue ha implementazioni sia compilate che interpretate. Ci sono interpreti per C ++ e ci sono compilatori per Python. (In effetti, tutte le implementazioni Python attualmente esistenti hanno compilatori.)
Jörg W Mittag,

4
La maggior parte delle moderne implementazioni linguistiche ad alte prestazioni combinano sia un interprete che un compilatore (o anche diversi compilatori) per ottenere le massime prestazioni. In realtà, è impossibile eseguire qualsiasi programma senza un interprete. Dopotutto, un compilatore è solo un programma che traduce un programma da una lingua a un'altra lingua. Ma ad un certo punto devi effettivamente eseguire il programma, che è fatto da un interprete (che può essere o meno implementato in silicio).
Jörg W Mittag,

10
@ JörgWMittag: tecnicamente hai ragione. Tuttavia, la maggior parte delle lingue sono state progettate per un contesto interpretato o per la compilazione completa. Scrivere un interprete per GW BASIC o Common Lisp è molto più semplice che scriverne uno per, diciamo, C ++ o C #; Python perde molti dei suoi punti vendita senza l'ambiente interattivo; scrivere un compilatore per PHP è dannatamente difficile e probabilmente terribilmente inefficiente, poiché l'eseguibile compilato dovrebbe contenere l'intero interprete PHP, a causa di eval () e costrutti simili - si potrebbe sostenere che un simile compilatore sarebbe barare.
martedì

2
@tdammers, sì. Possiamo ragionevolmente usare "linguaggio compilato" per indicare "linguaggio solitamente compilato". Ma questo non significa che PHP, Java, Python, Lua e C # siano tutti implementati come compilatori in bytecode. Tutte queste lingue hanno anche implementato JIT per loro. Quindi, davvero, non puoi davvero chiamare alcune di queste lingue compilate e alcune interpretate perché hanno la stessa strategia di implementazione.
Winston Ewert,

2
@BillyONeal, non vero almeno per Python. Puoi distribuire bytecode python ed eseguirlo senza il sorgente. Ma è vero che non puoi distribuire Python senza un compilatore.
Winston Ewert,

5

Non tutte le lingue compilate hanno un ciclo di modifica-compilazione-collegamento-corsa diretto.

Quello che stai incontrando è una caratteristica / limitazione di C ++ (o almeno implementazioni C ++).

Per fare qualsiasi cosa, è necessario archiviare il codice in file e creare un'immagine monolitica mediante un processo chiamato collegamento.

In particolare, è questo processo di collegamento monolitico che viene scambiato per la distinzione tra compilazione e interpretazione.

Alcune lingue eseguono tutte queste cose in modo molto più dinamico, eliminando il maldestro passaggio monolitico di collegamento, non eliminando la compilazione nel codice macchina. L'origine viene ancora compilata in file oggetto, ma questi vengono caricati in un'immagine di runtime, anziché essere collegati in un eseguibile monolitico.

Dici "ricarica questo modulo", e carica il sorgente e lo interpreta, o lo compila, a seconda del cambio di modalità.

La programmazione del kernel Linux ha un po 'di questo sapore anche se stai lavorando in C. Puoi ricompilare un modulo e caricarlo e scaricarlo. Ovviamente, sei ancora consapevole del fatto che stai producendo qualcosa di eseguibile, ed è gestito da un sistema di compilazione complesso, con ancora alcuni passaggi manuali. Ma il fatto è che alla fine è possibile scaricare e ricaricare solo quel piccolo modulo e non è necessario riavviare l'intero kernel.

Alcune lingue hanno una modularizzazione ancora più fine di così, e la costruzione e il caricamento avvengono all'interno del loro tempo di esecuzione, quindi è più fluido.


2

che diversione dalla domanda iniziale ... Un punto non menzionato è che la fonte di un programma Python è ciò che usi e distribuisci, dal punto di vista dell'utente È il programma. Tendiamo a semplificare le cose in categorie che non sono ben definite.

I programmi compilati sono generalmente considerati file autonomi di codice macchina. (è vero che spesso contiene collegamenti a librerie a collegamento dinamico associate a specifici sistemi operativi). Detto questo ... ci sono variazioni della maggior parte del linguaggio di programmazione che potrebbero essere descritte come compilate o interpretate.

Python non ha bisogno di un compilatore perché si basa su un'applicazione (chiamata interprete) che compila ed esegue il codice senza memorizzare il codice macchina creato in una forma a cui è possibile accedere o distribuire facilmente.


1

Tutti i linguaggi di programmazione richiedono la traduzione da concetti umani in un codice macchina di destinazione. Anche il linguaggio assembly deve essere tradotto in codice macchina. Tale traduzione di solito avviene nelle seguenti fasi:

Fase 1: analisi e traduzione (analisi) in un codice intermedio. Fase 2: traduzione del codice intermedio nel codice macchina target con segnaposto per riferimenti esterni. Fase 3: risoluzione dei riferimenti esterni e del packaging in un programma eseguibile dalla macchina.

Questa traduzione viene spesso definita pre-compilazione e "Just in time" (JIT) o compilazione runtime.

Linguaggi come C, C ++, COBOL, Fortran, Pascal (non tutti) e Assembly sono linguaggi precompilati che possono essere eseguiti direttamente dal sistema operativo senza la necessità di un interprete.

Vengono interpretati linguaggi come Java, BASIC, C # e Python. Usano tutti quel codice intermedio creato nella Fase 1, ma a volte differiscono nel modo in cui lo traducono in codice macchina. I moduli più semplici utilizzano quel codice intermedio per eseguire routine di codice macchina che eseguono il lavoro previsto. Altri compileranno il codice intermedio fino al codice macchina ed eseguiranno il fissaggio della dipendenza esterna durante il runtime. Una volta compilato può essere immediatamente eseguito. Inoltre, il codice macchina viene archiviato in una cache di codice macchina riutilizzabile precedentemente compilato che può essere successivamente riutilizzato se la funzione è necessaria nuovamente in un secondo momento. Se una funzione è già stata memorizzata nella cache, l'interprete non deve compilarla di nuovo.

La maggior parte delle lingue moderne di alto livello rientrano nella categoria interpretata (con JIT). Sono soprattutto i linguaggi più vecchi come C & C ++ a essere precompilati.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.