Python é incrível.
Surpreendentemente, essa é uma afirmação bastante ambígua. O que quero dizer com 'Python'? Refiro-me à interface abstrata do Python? Quero dizer CPython, a implementação comum do Python (e não deve ser confundida com Cython, que são semelhantes em seus nomes)? Ou quero dizer algo totalmente diferente? Talvez eu esteja me referindo indiretamente a Jython, IronPython ou PyPy. Ou talvez eu tenha ido ao extremo e esteja falando sobre RPython ou RubyPython (ambos são coisas muito, muito diferentes).
Embora as tecnologias mencionadas acima sejam chamadas de maneiras semelhantes e referenciadas da mesma maneira, algumas delas servem a propósitos completamente diferentes (ou, pelo menos, operam de maneiras completamente diferentes).
Ao longo do meu tempo trabalhando com Python, me deparei com toneladas dessas ferramentas. * Ython. Mas não foi até recentemente que dediquei tempo para entender o que eles são, como funcionam e por que são necessários (à sua maneira).
Neste artigo, vou começar do zero e percorrer várias implementações Python, concluindo com uma introdução detalhada ao PyPy, que acredito ser o futuro da linguagem.
Tudo começa com a compreensão do que realmente é 'Python'.
Se você tem um bom entendimento de código binário, máquinas virtuais e similares, sinta-se à vontade para pule esta parte .
Este é um ponto comum de confusão para iniciantes em Python.
A primeira coisa a saber é que 'Python' é uma interface. Existe um especificação sobre o que Python deve fazer e como devemos se comportar (como em qualquer interface). E existem várias implementações (como em qualquer interface).
A segunda coisa a saber é que 'interpretado' e 'compilado' são propriedades de um implementação , nenhum Interface .
Portanto, a questão não está bem formulada.
O Python é interpretado ou compilado? A questão não está muito bem formulada.Dito isso, para a implementação mais comum (CPython: escrito em C, geralmente chamado apenas de 'Python', e certamente o que você está usando se não tem ideia do que estou falando), a resposta é: interpretado , com algumas peças compilado. CPython compilar ** código-fonte python para * bytecode , e naquele momento interpretar esse bytecode, executando-o na hora.
* Nota: não é uma 'compilação' no sentido tradicional da palavra. Normalmente, dizemos que 'compilar' é pegar o código de alto nível e convertê-lo em código binário. Mas é uma espécie de 'compilação'.
Vamos examinar a resposta um pouco mais de perto, pois ela nos permitirá entender alguns dos conceitos que surgirão mais adiante neste artigo.
É muito importante entender a diferença entre bytecode e código binário (ou nativo), talvez melhor ilustrado com exemplos:
Em resumo: o código binário é mais rápido, mas o bytecode é mais portátil e seguro .
O código binário parece diferente, dependendo da sua máquina, mas o bytecode parece o mesmo em todas as máquinas. Você poderia dizer que o código binário é otimizado para sua configuração.
Voltando ao CPython, o processo no conjunto de ferramentas acontece da seguinte maneira:
Os iniciantes presumem que o Python é compilado a partir dos arquivos .pyc. Há alguma verdade nisso: o arquivo .pyc é compilado em bytecode, que é então interpretado. Portanto, se você executou o código Python e já tem um arquivo .pyc disponível, ele será executado mais rápido na segunda vez, pois não será necessário recompilar o bytecode.
Como mencionei anteriormente, Python tem várias implementações. Novamente, como mencionei antes, o mais comum é CPython. Esta é uma implementação Python escrita em C e é considerada a implementação 'padrão'.
Mas e as alternativas? Um dos mais proeminentes é Jython , uma implementação em Java que usa a JVM. Enquanto CPython produz bytecode para ser executado na VM CPython, Jython produz java bytecode para ser executado na JVM (é a mesma coisa que é produzida ao compilar um programa Java).
“Por que eu usaria uma implementação alternativa?” Você pode perguntar. Bem, para começar, aqueles diferentes implementações funcionam bem com diferentes conjuntos de tecnologias .
CPython torna muito fácil escrever extensões C para seu código Python porque no final ele é executado por um interpretador C. Por outro lado, Jython torna mais fácil trabalhar com outros programas Java: você pode importar qualquer classe Java sem muito esforço , evocando e usando suas classes Java em seus programas Jython. (Observação: se você não pensou nisso em detalhes, é uma loucura. Estamos em um ponto onde você pode misturar e fragmentar diferentes linguagens e compilá-las em um único núcleo. Conforme mencionado por Rostin , programas que misturam código Fortran e C já existem há algum tempo. Portanto, é claro que isso não é necessariamente algo novo. Mas ainda é ótimo.)
Por exemplo, este é um código Jython válido:
[Java HotSpot(TM) 64-Bit Server VM (Apple Inc.)] on java1.6.0_51 >>> from java.util import HashSet >>> s = HashSet(5) >>> s.add('Foo') >>> s.add('Bar') >>> s [Foo, Bar]
IronPython é outra implementação Python popular, escrita inteiramente em C # e voltada para a tecnologia .NET. Em particular, ele é executado com o que poderia ser chamado de Máquina Virtual .NET, Common Language Runtime (CLR) da Microsoft, comparável ao JVM.
Você poderia dizer que Jython: Java :: IronPython: C #. Eles são executados em suas respectivas VMs, você pode importar classes C # em seu código IronPython e classes Java de seu código Jython, etc.
É totalmente possível sobreviver sem nunca tocar em uma implementação Python não CPython. Mas existem vantagens obtidas com a mudança, muitas delas dependem da tecnologia que você usa. Você usa muitas linguagens baseadas em JVM? Jython pode ser para você. Tudo o que você faz é sobre a tecnologia .NET? Talvez você deva experimentar IronPython (e talvez já tenha).
A propósito: embora isso não seja um motivo para usar uma implementação diferente, observe que essas implementações diferem no comportamento além de como tratam seu código-fonte em Python. No entanto, essas diferenças são geralmente menores, dissolvendo-se ou emergindo com o tempo enquanto essas implementações estão em desenvolvimento ativo. Por exemplo, IronPython usar strings Unicode por padrão ; No entanto, CPython, por padrão usa ASCII para versões 2.x (falha com um erro de codificação UnicodeEncodeError para caracteres não ASCII), mas suporta strings Unicode por padrão para as versões 3.x .
Portanto, temos uma implementação Python escrita em C, uma em Java e outra em C #. O próximo passo lógico: uma implementação Python escrita em… Python. (O leitor instruído achará essa notação um pouco enganosa.)
É aqui que as coisas ficam confusas. Primeiro, vamos discutir a compilação Just-in-Time (JIT).
Lembre-se de que o código binário é muito mais rápido do que o bytecode. Bem, e se pudéssemos compilar algumas partes do nosso bytecode e depois executá-lo como código nativo? Teríamos que pagar algum preço ao compilar para bytecode (por exemplo, tempo), mas se o resultado fosse mais rápido, seria ótimo! Essa é a motivação por trás da compilação JIT, uma técnica híbrida que combina os benefícios de interpretadores e compiladores. Em termos básicos, o JIT quer usar a compilação para acelerar um sistema interpretado.
Por exemplo, uma abordagem comum adotada pela compilação JIT:
É disso que se trata o PyPy: trazer JIT para Python (consulte o Apêndice para ver os esforços anteriores). Existem, é claro, outros objetivos: o PyPy visa ser multiplataforma, com baixo consumo de memória e independente de todas as tecnologias. Mas o JIT realmente se vende. Na média de um punhado de testes de tempo, é dito que melhora o desempenho por um fator de 6,27 . Para uma discussão mais aprofundada, consulte este gráfico do PyPy Speed Center :
PyPy tem um grande potencial e, neste ponto, é muito compatível com CPython (como este que pode executar Flask, Django, etc. )
Mas há muita confusão em torno do PyPy (veja, por exemplo, esta proposta absurda de criar um PyPyPy ... ) Na minha opinião, isso ocorre principalmente porque PyPy é atualmente duas coisas:
Um interpretador Python escrito em RPython (não Python (eu menti antes). RPython é um subconjunto de Python com tipos estáticos. Em Python, é ' praticamente impossivel ”Raciocine rigorosamente sobre os tipos (Por que é tão difícil? Bem, considere o fato de que:
x = random.choice([1, 'foo'])
seria um código Python válido (créditos para Gesto ) Que tipo é x? Como podemos raciocinar sobre os tipos de variáveis quando os tipos nem mesmo são estritamente impostos?). Com RPython, você sacrifica alguma flexibilidade, mas em troca é muito mais fácil raciocinar sobre gerenciamento de memória e assim por diante, o que permite otimizações.
Um compilador que compila o código RPython para vários fins e adiciona JIT. A plataforma padrão é C, por exemplo, um compilador RPython-para-C, mas você também pode apontar para JVM e outros.
Apenas para maior clareza, vou me referir a eles como PyPy (1) e PyPy (2).
Por que você precisaria dessas duas coisas e por que sob o mesmo teto? Pense desta forma: PyPy (1) é um interpretador escrito em RPython. Portanto, ele pega o código Python do usuário e o compila em bytecode. Mas o próprio interpretador (escrito em RPython) deve ser interpretado por outra implementação Python para ser executado, certo?
Bem, poderíamos apenas usar CPython para executar o interpretador. Mas isso não seria rápido o suficiente.
Em vez disso, a ideia é usar PyPy (2) (também conhecido como RPython Toolchain) -RPython toolset) para compilar o interpretador PyPy para o código que outra plataforma (por exemplo, C, JVM ou CLI) pode executar em nossa máquina, adicionando também JIT. É mágico: o PyPy adiciona dinamicamente JIT a um interpretador, gerando seu próprio compilador! (Novamente, isso é loucura: estamos compilando um interpretador e adicionando outro compilador separado separadamente.)
No final, o resultado é um executável autônomo que interpreta o código-fonte do Python e explora as otimizações JIT. Que é exatamente o que queríamos! É um grande bocado, mas talvez este diagrama ajude:
Para reiterar, a verdadeira beleza do PyPy é que podemos escrever um punhado de interpretadores Python diferentes em RPython sem nos preocuparmos com o JIT (exceto por algumas sugestões). PyPy então implementaria o JIT para nós usando o conjunto de ferramentas RPython / PyPy (2).
Na verdade, se formos ainda mais abstratos, você poderia, teoricamente, escrever um intérprete para qualquer linguagem, alimente PyPy com ela e obtenha um JIT para essa linguagem. Isso ocorre porque o PyPy se concentra na otimização do interpretador atual, e não nos detalhes do idioma que está interpretando.
Você poderia, teoricamente, escrever um intérprete para * qualquer * linguagem, alimentar o PyPy com ele e obter um JIT para essa linguagem.Vagando um pouco, gostaria de mencionar que o JIT em si é absolutamente fascinante. Ele usa uma técnica chamada rastreamento (ou rastreamento), que é realizada da seguinte maneira :
Para mais informacao, este documento é altamente acessível e muito interessante.
Para concluir: usamos o compilador PyPy RPython-a-C (ou outra plataforma) para compilar o interpretador PyPy RPython implementado.
Por que é tão legal? Por que vale a pena perseguir essa ideia maluca? Creio que Alex Gaynor ele descreveu muito bem em seu Blog : '[PyPy é o futuro] porque oferece melhor velocidade, mais flexibilidade e é uma plataforma melhor para o crescimento do Python.'
Em resumo:
Conteúdo traduzido por Pablo Fabregat, sócio de TransBunko , um mercado de traduções técnicas.