Por que o Python não precisa de um compilador?

29

Apenas me perguntando (agora que eu comecei com C ++, que precisa de um compilador) por que o Python não precisa de um compilador?

Acabei de inserir o código, salvá-lo como um executivo e executá-lo. Em C ++, eu tenho que fazer builds e todas essas outras coisas divertidas.

Billjk
fonte
4
Python é apenas uma linguagem com muitas implementações. O Iron Python é compilado da mesma maneira que C # e C ++ são compilados, e pode haver outras implementações como essa.
Job
11
C # e C ++ não são compilados da mesma maneira - embora você possa argumentar que ambos acabam sendo instruções de máquina eventualmente, mas se o fizer, poderá dizer que o BASIC também é compilado da mesma maneira.
Gbjbaanb
7
@gbjbaanb, mas novamente o inglês não é compilado e a análise semântica de uma frase pode render dois resultados igualmente válidos, e o acima pode ser lido como "python de ferro é compilado assim como C # e C ++ é compilado"
Rune FS
Qual plataforma / software você está usando para escrever seu código Python? Se você gravar um arquivo .py, ele não é um executável. Ainda é um arquivo de código-fonte. Na linha de comando, você está usando o pythoncomando para interpretar o arquivo .py ou, se você usa o IDLE ou o Eclipse, o IDE faz isso por você.
Rick Henderson

Respostas:

68

Python tem um compilador! Você simplesmente não percebe porque é executado automaticamente. Você pode dizer que está lá: veja os arquivos .pyc(ou .pyose o otimizador estiver ativado) que são gerados para os módulos que você import.

Além disso, ele não é compilado no código da máquina nativa. Em vez disso, ele compila para um código de bytes usado por uma máquina virtual. A máquina virtual é ela própria um programa compilado. Isso é muito parecido com o funcionamento do Java; tão semelhante, de fato, que existe uma variante Python ( Jython ) que compila o código de bytes da Java Virtual Machine! Há também o IronPython , que compila com o CLR da Microsoft (usado pelo .NET). (O compilador de código de bytes Python normal às vezes é chamado de CPython para desambiguá-lo dessas alternativas.)

O C ++ precisa expor seu processo de compilação porque a própria linguagem está incompleta; ele não especifica tudo o que o vinculador precisa saber para criar seu programa, nem pode especificar opções de compilação de forma portável (alguns compiladores permitem que você use #pragma, mas isso não é padrão). Então você tem que fazer o resto do trabalho com makefiles e possivelmente com o inferno automático (autoconf / automake / libtool). Este é realmente apenas um resquício de como C fez isso. E C fez dessa maneira porque simplificou o compilador, o que é um dos principais motivos pelos quais ele é tão popular (qualquer um poderia criar um compilador C simples nos anos 80).


Algumas coisas que podem afetar a operação do compilador ou vinculador, mas não são especificadas na sintaxe do C ou C ++:

  • resolução de dependência
  • requisitos de biblioteca externa (incluindo ordem de dependência)
  • nível otimizador
  • configurações de aviso
  • versão de especificação de idioma
  • mapeamentos de vinculador (qual seção vai para onde no programa final)
  • arquitetura de destino

Alguns deles podem ser detectados, mas não podem ser especificados; por exemplo, eu posso detectar com qual C ++ está sendo usado __cplusplus, mas não posso especificar que C ++ 98 é aquele usado para o meu código no próprio código; Eu tenho que passá-lo como um sinalizador para o compilador no Makefile, ou fazer uma configuração em uma caixa de diálogo.

Embora você possa pensar que existe um sistema de "resolução de dependência" no compilador, gerando automaticamente registros de dependência, esses registros dizem apenas quais arquivos de cabeçalho um determinado arquivo de origem usa. Eles não podem indicar quais módulos de código-fonte adicionais são necessários para vincular a um programa executável, porque não há uma maneira padrão em C ou C ++ para indicar que um determinado arquivo de cabeçalho é a definição de interface para outro módulo de código-fonte, em vez de apenas um monte de linhas que você deseja exibir em vários lugares para não se repetir. Existem tradições nas convenções de nomenclatura de arquivos, mas elas não são conhecidas ou impostas pelo compilador e vinculador.

Vários deles podem ser definidos usando #pragma, mas isso não é padrão, e eu estava falando sobre o padrão. Todas essas coisas podem ser especificadas por um padrão, mas não têm o interesse de compatibilidade com versões anteriores. A sabedoria predominante é que makefiles e IDEs não estão quebrados, então não os conserte.

Python lida com tudo isso na linguagem. Por exemplo, importespecifica uma dependência explícita do módulo, implica a árvore de dependências e os módulos não são divididos em arquivos de cabeçalho e de origem (por exemplo, interface e implementação).

Mike DeSimone
fonte
3
A implementação C do Python é CPython , Cython é algo diferente.
Greg Hewgill
4
Outras razões pelas quais C compilou no código da máquina foram que ele pretendia ser pouco mais que um montador glorificado, porque os intérpretes de bytecode eram tecnicamente inviáveis ​​no hardware que possuíam e porque uma das tarefas mais importantes era escrever um kernel do sistema operacional.
26612 tdammers #
2
@BillyONeal, com a grande exceção de que, em c / c ++, você como programador precisa fazer as coisas de uma certa maneira (makefiles ou despejar tudo no mesmo blob) em python, você apenas faz seu trabalho e o compilador junto com a VM cuida do resto
Rune FS
3
"O C ++ precisa expor seu processo de compilação porque a própria linguagem está incompleta" Er, o que?
Lightness Races com Monica
3
Você leu a parte logo depois , certo? "ele não especifica tudo o que o vinculador precisa saber para criar seu programa, nem pode especificar opções de compilação de forma portável." Você não pode simplesmente criar qualquer arquivo C ++ alimentando-o em um compilador; freqüentemente você precisa fornecer metadados, como sinalizadores de compilação, incluir caminhos etc. Esses metadados não são especificados pelo padrão e não são portáveis, e é por isso que precisamos arrastar outras coisas, como make, cmake, Visual Studio ou o que termine o trabalho. Portanto, o padrão precisa chamar algumas coisas, como na unidade de compilação, e outras, como em todo o programa.
21430 Mike DeSimone #
7

Python é uma linguagem interpretada. Isso significa que existe um software no seu computador que lê o código Python e envia as "instruções" para a máquina. O artigo da Wikipedia sobre idiomas interpretados pode ser interessante.

Quando uma linguagem como C ++ (uma linguagem compilada) é compilada, significa que ela é convertida em código de máquina para ser lida diretamente pelo hardware quando executada. O artigo da Wikipedia sobre idiomas compilados pode fornecer um contraste interessante.

Zenon
fonte
21
Não existe linguagem interpretada ou compilada. Uma linguagem é um conjunto abstrato de regras matemáticas. Um idioma não é compilado ou interpretado. Uma linguagem simplesmente é . Compilação e interpretação são características do compilador ou intérprete (duh!), Não da linguagem. Todo idioma pode ser implementado com um compilador e todo idioma pode ser implementado com um intérprete. A maioria dos idiomas possui implementações compiladas e interpretadas. Existem intérpretes para C ++ e compiladores para Python. (Na verdade, todas as implementações Python actualmente existentes têm os compiladores.)
Jörg W Mittag
4
A maioria das implementações modernas de linguagem de alto desempenho combina um intérprete e um compilador (ou até vários compiladores) para obter o máximo desempenho. Na verdade, é impossível executar qualquer programa sem um intérprete. Afinal, um compilador é apenas um programa que traduz um programa de um idioma para outro idioma. Mas em algum momento você precisa executar o programa, o que é feito por um intérprete (que pode ou não ser implementado em silício).
Jörg W Mittag
10
@ JörgWMittag: Você está tecnicamente certo. No entanto, a maioria dos idiomas foi projetada para um contexto interpretado ou para compilação completa. Escrever um intérprete para GW BASIC ou Common Lisp é muito mais fácil do que escrever um para, digamos, C ++ ou C #; Python perde muitos de seus pontos de venda sem o ambiente interativo; escrever um compilador para PHP é muito difícil e provavelmente terrivelmente ineficiente, pois o executável compilado teria que conter todo o interpretador PHP, devido a eval () e construções semelhantes - alguém poderia argumentar que esse compilador estaria trapaceando.
tdammers
2
@ Tdammers, sim. Podemos usar razoavelmente "linguagem compilada" para significar "linguagem geralmente compilada". Mas isso não leva ao ponto de que PHP, Java, Python, Lua e C # são todos implementados como compiladores no bytecode. Todos esses idiomas também tiveram o JIT implementado para eles. Então, realmente, você não pode realmente chamar algumas dessas linguagens compiladas e outras interpretadas porque elas têm a mesma estratégia de implementação.
Winston Ewert
2
@ BillyONeal, não é verdade, pelo menos para python. Você pode distribuir o bytecode python e executá-lo sem a fonte. Mas é verdade que você não pode distribuir python sem um compilador.
Winston Ewert
5

Nem todos os idiomas compilados têm um ciclo de edição-compilação-link-execução na sua cara.

O que você está enfrentando é um recurso / limitação do C ++ (ou pelo menos implementações do C ++).

Para fazer qualquer coisa, você deve armazenar seu código em arquivos e criar uma imagem monolítica por um processo chamado vinculação.

Em particular, é esse processo de ligação monolítica que é confundido com a distinção entre compilar e interpretar.

Algumas linguagens fazem tudo isso de maneira muito mais dinâmica, eliminando a desajeitada etapa de vinculação monolítica, e não eliminando a compilação no código da máquina. A fonte ainda é compilada para arquivos de objetos, mas eles são carregados em uma imagem de tempo de execução, em vez de vinculados a um executável monolítico.

Você diz "recarregar este módulo" e ele carrega a fonte e a interpreta, ou a compila, dependendo de alguma opção de modo.

A programação do kernel Linux tem um pouco desse sabor, mesmo que você esteja trabalhando no C. Você pode recompilar um módulo e carregá-lo e descarregá-lo. Obviamente, você ainda está ciente de que está produzindo alguma coisa executável, e é gerenciada por um sistema de compilação complexo, com algumas etapas manuais. Mas o fato é que, no final, você pode descarregar e recarregar apenas esse pequeno módulo e não precisa reiniciar o kernel inteiro.

Alguns idiomas têm uma modularização ainda mais refinada do que isso, e a construção e o carregamento são feitos dentro do tempo de execução, por isso é mais fácil.

Kaz
fonte
2

que desvio da pergunta inicial ... Um ponto não mencionado é que a fonte de um programa python é o que você usa e distribui; da perspectiva do usuário, é o programa. Tendemos a simplificar as coisas em categorias que não estão bem definidas.

Os programas compilados são geralmente considerados arquivos independentes de código de máquina. (geralmente contém links para bibliotecas de links dinâmicos associados a sistemas operacionais específicos). Dito isto ... há variações da maioria das linguagens de programação que podem ser descritas como compiladas ou interpretadas.

O Python não precisa de um compilador, porque conta com um aplicativo (chamado de intérprete) que compila e executa o código sem armazenar o código da máquina que está sendo criado em um formulário que você pode acessar ou distribuir facilmente.

Stephen Robinson
fonte
1

Todas as linguagens de programação requerem tradução de conceitos humanos em um código de máquina de destino. Até a linguagem assembly deve ser traduzida em código de máquina. Essa tradução geralmente ocorre nas seguintes fases:

Fase 1: Análise e tradução (análise) em um código intermediário. Fase 2: tradução do código intermediário no código da máquina de destino com espaços reservados para referências externas. Fase 3: Resolução das referências externas e empacotamento em um programa executável por máquina.

Essa tradução geralmente é chamada de pré-compilação e "Just in time" (JIT) ou compilação em tempo de execução.

Idiomas como C, C ++, COBOL, Fortran, Pascal (não todos) e Assembly são idiomas pré-compilados que podem ser executados diretamente pelo sistema operacional sem a necessidade de um intérprete.

Idiomas como Java, BASIC, C # e Python são interpretados. Todos eles usam esse código intermediário criado na Fase 1, mas às vezes diferem na maneira como o traduzem em código de máquina. Os formulários mais simples usam esse código intermediário para executar rotinas de código de máquina que executam o trabalho esperado. Outros compilarão o código intermediário até o código da máquina e farão a correção da dependência externa durante o tempo de execução. Uma vez compilado, pode ser executado imediatamente. O código da máquina também é armazenado em um cache de código de máquina reutilizável compilado anteriormente que pode ser reutilizado posteriormente se a função for necessária novamente mais tarde. Se uma função já tiver sido armazenada em cache, o intérprete não precisará compilá-la novamente.

A maioria dos idiomas modernos de alto nível se enquadra na categoria interpretada (com JIT). A maioria das linguagens antigas, como C e C ++, é pré-compilada.

Mark Baker
fonte