A linguagem de programação Python, que surgiu em 1994, popularizou-se de fato a partir da virada do milênio. Uma das medidas do sucesso de uma linguagem é o número de implementações. A mais conhecida e usada delas é a CPython. Também houve projetos bem-sucedidos, como o Jython (linguagem Python operando no tempo de execução Java) e IronPython (linguagem Python operando na plataforma .NET). Todas essas implementações são software livre, e o Python sempre teve grande presença na área do software livre.
Um dos objetivos de longa data da implementação Python é suportar o design de linguagem pura – “autoinicializar” a definição do Python especificando a linguagem em seus próprios termos – e não nos termos de outras linguagens, como C e Java. O projeto PyPy é uma implementação do Python que supre essa necessidade. PyPy significa “Python implementado em Python” – embora, na verdade, seja implementado em um subconjunto do Python chamado RPython. Mais precisamente, o PyPy é um tempo de execução à parte, ao qual é possível conectar qualquer linguagem.
O design despojado da linguagem PyPy permite desenvolver em otimizadores de baixo nível, trazendo um enorme benefício à otimização. Especificamente, o PyPy integra um compilador just-in-time (JIT). É a mesma tecnologia famosa que revolucionou o desempenho do Java na forma do HotSpot, adquirida pela Sun Microsystems da Animorphic no início da década de 2000 e incorporada à implementação de Java da empresa, tornando a linguagem prática para a maioria dos usos. O Python já é prático para muitos usos, mas o desempenho é a reclamação mais frequente. O compilador Just-In-Time de rastreio do PyPy já está mostrando que pode revolucionar o desempenho dos programas de Python. Embora o projeto esteja em uma fase que eu consideraria beta final, ele já é uma ferramenta essencial para o programador em Python e um acréscimo muito útil à caixa de ferramentas de qualquer desenvolvedor.
Neste artigo, eu apresento o PyPy sem supor que você tem ampla experiência com o Python.
Introdução
Primeiramente, não confunda PyPy com PyPI. São projetos muito diferentes. PyPI é o Python Package Index, um site e sistema para obter pacotes de Python de terceiros para complementar a biblioteca padrão. Ao acessar o site coreto do PyPy (consulte “Recursos”) você verá que os desenvolvedores fizeram o possível para que a maioria dos usuários tivesse facilidade de experimentar as coisas. Se você tem Linux®, Mac ou Windows (com exceção do Windows 64, que ainda não é suportado) em um hardware recente, deve ser capaz de fazer o download e executar um dos pacotes binários.
A versão atual do PyPy é a 1.8, que implementa totalmente o Python 2.7.2 – ou seja, deve ser compatível, em termos de recursos e comportamento da linguagem, com essa versão do CPython. Entretanto, já é bem mais rápida que o CPython 2.7.2 em muitos usos dos quais se tomou a referência. Isso é o que realmente atrai o nosso interesse. A sessão a seguir mostra como eu instalei o PyPy na minha caixa Ubuntu 11.04. Foi capturado de um release anterior do PyPy, mas o PyPy 1.8 dá resultados semelhantes.
$ cd Downloads/
$ wget https://bitbucket.org/pypy/pypy/downloads/pypy-1.6-linux.tar.bz2
$ cd ../.local
$ tar jxvf ~/Downloads/pypy-1.6-linux.tar.bz2
$ ln -s ~/.local/pypy-1.6/bin/pypy ~/.local/bin/
Agora é necessário atualizar $PATH para incluir ~/.local/bin/. Depois de instalar o PyPy, eu recomendo instalar também o Distribute e o Pip, para facilitar a instalação de pacotes adicionais (embora eu não aborde esse assunto neste artigo, também é conveniente usar o Virtualenv, que é uma forma de manter os ambientes Python separados e despojados). A sessão a seguir demonstra a configuração do Distribute e do Pip.
$ wget http://python-distribute.org/distribute_setup.py
$ wget https://raw.github.com/pypa/pip/master/contrib/get-pip.py
$ pypy distribute_setup.py
$ pypy get-pip.py
Você deve encontrar arquivos de biblioteca instalados em ~/.local/pypy-1.8/site-packages/ e executáveis em ~/.local/pypy-1.8/bin. Sendo assim, é conveniente incluir os executáveis no $PATH. Além disso, certifique-se de usar o pip que acabou de ser instalado, e não o pip para todo o sistema. Depois disso, é possível instalar os pacotes de terceiros usados mais adiante neste artigo.
$ pip install html5lib
$ pip install pyparsing
A Listagem 1 mostra a saída do interpretador do PyPy depois da chamada do “easter egg” do Python import this.
uche@malatesta:~$ pypy
Python 2.7.1 (d8ac7d23d3ec, Aug 17 2011, 11:51:18)
[PyPy 1.6.0 with GCC 4.4.3] on linux2
Type "help", "copyright", "credits" or "license" for more information.
And now for something completely different: ``__xxx__ and __rxxx__ vs operation
slots: particle quantum superposition kind of fun''
>>>> import this
The Zen of Python, by Tim Peters
Beautiful is better than ugly.
Explicit is better than implicit.
Simple is better than complex.
Complex is better than complicated.
Flat is better than nested.
Sparse is better than dense.
Readability counts.
Special cases aren't special enough to break the rules.
Although practicality beats purity.
Errors should never pass silently.
Unless explicitly silenced.
In the face of ambiguity, refuse the temptation to guess.
There should be one-- and preferably only one --obvious way to do it.
Although that way may not be obvious at first unless you're Dutch.
Now is better than never.
Although never is often better than *right* now.
If the implementation is hard to explain, it's a bad idea.
If the implementation is easy to explain, it may be a good idea.
Namespaces are one honking great idea -- let's do more of those!
>>>>
Censo de links
Como uma ilustração simples do PyPy em ação, eu apresento um programa para analisar uma página da web e imprimir uma lista dos links expressos na página. Essa é a ideia básica por trás do software em teia de aranha, que segue uma teia de links de página em página com algum propósito.
Para a análise, escolhi o html5lib, uma biblioteca de análise puro Python destinada a implementar os algoritmos de análise do grupo WHAT-WG que está definindo a especificação HTML5. O HTML5 deve ser compatível com as versões anteriores, até mesmo com páginas da web muito quebradas. Sendo assim, o html5lib é um kit de ferramentas de análise de HTML de boa qualidade para uso geral. Além disso, foi realizado o teste de benchmark no CPython e PyPy, o qual constatou que ele é bem mais rápido PyPy.
A Listagem 2 analisa uma página da web especificada e imprime os links da mesma linha por linha. Você especifica a URL da página de destino na linha de comando, por exemplo: pypy listing1.py http://www.ibm.com/developerworks/opensource/.
#!/usr/bin/env pypy
#Import the needed libraries for use
import sys
import urllib2
import html5lib
#List of tuples, each an element/attribute pair to check for links
link_attrs = [
('a', 'href'),
('link', 'href'),
]
#This function is a generator, a Python construct that can be used as a sequence.
def list_links(url):
'''
Given a URL parse the HTML and yield a sequence of link strings
as they are found on the page.
'''
#Open the URL and get back a stream of the content
stream = urllib2.urlopen(url)
#Parse the HTML content according to html5lib conventions
tree_builder = html5lib.treebuilders.getTreeBuilder('dom')
parser = html5lib.html5parser.HTMLParser(tree=tree_builder)
doc = parser.parse(stream)
#In the outer loop, go over each element/attribute set
for elemname, attr in link_attrs:
#In the inner loop, go over the matches of the current element name
for elem in doc.getElementsByTagName(elemname):
#If the corresponding attribute is found, yield it in sequence
attrvalue = elem.getAttribute(attr)
if attrvalue:
yield attrvalue
return#Read the URL to parse from the first command line argument
#Note: Python lists start at index 0, but as in UNIX convention the 0th
#Command line argument is the program name itself
input_url = sys.argv[1]
#Set up the generator by calling it with the URL argument, then iterate
#Over the yielded link strings, printing each
for link in list_links(input_url):
print link
Eu comentei bastante o código e não espero que o leitor tenha um conhecimento profundo sobre Python, mas você deve conhecer o básico – por exemplo, como a indentação é usada para expressar o fluxo de controle. Consulte Recursos para obter tutoriais relevantes de Python.
Por uma questão de simplicidade, evitei algumas convenções nesses programas, mas usei um recurso avançado que me parece muito útil, inclusive para o programador iniciante. A função list_links é chamada de gerador. É uma função que age como uma sequência na qual ela computa e oferece os itens um por um. As instruções yield são fundamentais neste caso, fornecendo a sequência de valores.
Uma raspagem de tela ainda mais complexa
A maioria das tarefas de análise de páginas da web é mais complexa do que a simples localização e exibição de links, e há varias bibliotecas que podem ajudar nas tarefas típicas de “raspagem de tela”. O Pyparsing é um kit de ferramentas de análise puro Python de uso geral que inclui alguns recursos para suportar a análise de HTML.
No próximo exemplo, irei demonstrar como raspar uma lista de artigos provenientes de uma página de índice do IBM developerWorks. Consulte a Figura 1 para ver uma captura de tela da página de destino. A Listagem 3 é um registro de amostra em HTML.
<tbody>
<tr>
<td>
<a href="http://www.ibm.com/developerworks/opensource/library/os-wc3jam/index.html">
<strong>Join the social business revolution</strong></a>
<div>
As mídias sociais se tornaram um negócio social e todos os envolvidos,
desde a liderança empresarial até os desenvolvedores de software, precisam entender as
ferramentas e técnicas que serão necessárias.
O World Wide Web Consortium (W3C) realizará um evento de mídia social para discutir as
normas relevantes e os requisitos para o curto e longo prazo.
</div>
</td>
<td>Articles</td>
<td class="dw-nowrap">03 Nov 2011</td>
</tr>
</tbody>
A Listagem 4 é o código para analisar esta página. Novamente, eu procuro comentar bastante, mas há alguns novos conceitos-chave que eu irei abordar depois da listagem.
#!/usr/bin/env pypy
#Import the needed built-in libraries for use
import sys
import urllib2
from greenlet import greenlet
#Import what we need from pyparsing
from pyparsing import makeHTMLTags, SkipTo
def collapse_space(s):
'''
Strip leading and trailing space from a string, and replace any run of whitespace
within with a single space
'''
#Split the string according to whitespace and then join back with single spaces
#Then strip leadig and trailing spaces. These are all standard Python library tools
return ' '.join(s.split()).strip()
def handler():
'''
Simple coroutine to print the result of a matched portion from the page
'''
#This will be run the first time the code switches to this greenlet function
print 'A list of recent IBM developerWorks Open Source Zone articles:'
#Then we get into the main loop
while True:
next_tok = green_handler.parent.switch()
print ' *', collapse_space(data.title), '(', data.date, ')', data.link.href
#Turn a regular function into a greenlet by wrapping it
green_handler = greenlet(handler)
#Switch to the handler greenlet the first time to prime it
green_handler.switch()
#Read the search starting page
START_URL = "http://www.ibm.com/developerworks/opensource/library/"
stream = urllib2.urlopen(START_URL)
html = stream.read()
stream.close()
#Set up some tokens for HTML start and end tags
div_start, div_end = makeHTMLTags("div")
tbody_start, tbody_end = makeHTMLTags("tbody")
strong_start, strong_end = makeHTMLTags("strong")
article_tr, tr_end = makeHTMLTags("tr")
td_start, td_end = makeHTMLTags("td")
a_start, a_end = makeHTMLTags("a")
#Put together enough tokens to narrow down the data desired from the page
article_row = ( div_start + SkipTo(tbody_start)
+ SkipTo(a_start) + a_start('link')
+ SkipTo(strong_start) + strong_start + SkipTo(strong_end)("title")
+ SkipTo(div_start) + div_start + SkipTo(div_end)("summary") + div_end
+ SkipTo(td_start) + td_start + SkipTo(td_end)("type") + td_end
+ SkipTo(td_start) + td_start + SkipTo(td_end)("date") + td_end
+ SkipTo(tbody_end)
)
#Run the parser over the page. scanString is a generator of matched snippets
for data, startloc, endloc in article_row.scanString(html):
#For each match, hand it over to the greenlet for processing
green_handler.switch(data)
Eu configurei a Listagem 4 de propósito para apresentar os recursos de Stackless Python do PyPy. Em poucas palavras, é uma implementação antiga e alternativa do Python que permite fazer experiências com recursos avançados de fluxo de controle. A maioria dos recursos do Stackless não entrou nas outras implementações do Python por causa das limitações em outros tempos de execução, que não existem no PyPy. Os greenlets são um exemplo disso. Os greenlets são como encadeamentos muito leves que são convertidos em multitarefa de forma cooperativa por chamadas explícitas para passar o conteúdo de um greenlet a outro. Os greenlets permitem fazer algumas coisas interessantes que os geradores permitem – e muito mais.
Na Listagem 4 eu uso greenlets para definir uma corrotina, uma função cuja operação está entrelaçada com outras, de uma forma que torna o fluxo fácil de desenvolver e seguir. Muitas vezes, os greenlets são usados em situações nas quais a programação mais convencional usa retornos de chamada, como os sistemas movidos a eventos. Em vez de chamar um retorno de chamada, você alterna o contexto para uma corrotina. O principal benefício de recursos desse tipo é o fato de permitir estruturar programas para alta eficiência sem um gerenciamento de estado complicado.
A Listagem 4 fornece uma amostra das corrotinas e dos greenlets em geral, mas é um conceito fácil de introduzir no contexto do PyPy, que vem com greenlets e outros recursos do Stackless. Na Listagem 4, sempre que o pyparsing corresponde a um registro, o greenlet é chamado para processá-lo.
Isto é uma amostra da saída da Listagem 4.
A list of recent IBM developerWorks Open Source Zone articles:
* Join the social business revolution ( 03 Nov 2011 )
http://www.ibm.com/developerworks/opensource/library/os-wc3jam/index.html
* Spark, an alternative for fast data analytics ( 01 Nov 2011 )
http://www.ibm.com/developerworks/opensource/library/os-spark/index.html
* Automate development and management of cloud virtual machines ( 29 Oct 2011 )
http://www.ibm.com/developerworks/cloud/library/cl-automatecloud/index.html
Por que e por que não
Eu adotei a abordagem da Listagem 4 de propósito, mas irei começar com um aviso: sempre é perigoso tentar processar HTML sem ferramentas muito especializadas. Apesar de não ser tão ruim quanto o XML – no qual o fato de não usar um analisador em conformidade é um antipadrão – o HTML é muito complexo e complicado, até mesmo nas páginas que estão em conformidade com o padrão. A maioria delas não está. Se for necessária uma análise de HTML para uso geral, o html5lib é uma opção melhor. Dito isso, a raspagem da web geralmente é uma operação especializada na qual você apenas extrai informações de acordo com a situação específica. Para esse uso limitado, o pyparsing é bom e oferece alguns recursos interessantes para ajudar.
Os motivos pelos quais eu introduzi os greenlets, que não são estritamente necessários na Listagem 4, ficam mais evidentes à medida que você expande o código desse tipo a muitos cenários concretos. Em situações nas quais você multiplexa a análise e processa com outras operações, a abordagem dos greenlets possibilita a estruturação do processamento de forma semelhante aos canais de linha de comando UNIX. Em casos nos quais a situação é complicada pelo trabalho com diversas páginas de origem, há mais um problema: o fato de que as operações de urllib2 não são assíncronas e todo o programa é bloqueado sempre que acessa uma página da web. A resolução desse problema está além do escopo deste artigo, mas o uso do fluxo de controle avançado na Listagem 4 deve habituá-lo a pensar bem para juntar esses aplicativos avançados tendo em vista o desempenho.
Recapitulação
O PyPy é um projeto mantido de forma ativa e certamente é um alvo móvel, mas já é possível fazer muita coisa com ele, e o alto nível de compatibilidade com o CPython significa que você provavelmente contará com uma plataforma de backup estabelecida para o seu trabalho se começar a fazer experiências. Neste artigo, você aprendeu o suficiente para começar. Além disso, despertamos o seu interesse pelos recursos muito interessantes de Stackless do PyPy. Acredito que você terá uma boa surpresa com o desempenho do PyPy e, o que é mais importante, ele abre novas formas de ver a programação para ser elegante sem sacrificar a velocidade.
Recursos
Aprender
- Outros recursos importantes do PyPy: a página de desempenho e o wiki de compatibilidade, que controla a compatibilidade das bibliotecas conhecidas do Python com o PyPy.
- Veja uma introdução suave ao Python na série “Descobrir Python” (developerWorks, 2005-2006), de Robert Brunner.
- Saiba mais sobre o HTML5 com a série “HTML5 fundamentals” (developerWorks, 2011), de Grace Walker.
- Confira o software livre do developerWorks para obter os recursos necessários para participar do interessante mundo do software livre.
- Fique por dentro dos eventos técnicos e webcasts do developerWorks com ênfase em uma série de produtos IBM e tópicos do segmento de mercado de TI.
- Participe de um Resumo de instruções do developerWorks para se atualizar rapidamente sobre produtos e ferramentas IBM e tendências do segmento de mercado de TI.
- Siga o developerWorks no Twitter.
- Acompanhe as Demos do developerWorks que abrangem desde demos de instalação e configuração de produtos para iniciantes até funcionalidades avançadas para desenvolvedores experientes.
Obter produtos e tecnologias
- Saiba mais sobre o PyPy ou vá diretamente à página de downloads.
Discutir
- Participe da comunidade do developerWorks. Entre em contato com outros usuários do developerWorks e explore os blogs, fóruns, grupos e wikis voltados para desenvolvedores.
***
Sobre o autor: Uche Ogbuji é sócio da Zephira , onde supervisiona a criação de sofisticados catálogos da web e outros bancos de dados ricamente contextuais. Ele possui uma longa história de pioneirismo em tecnologias avançadas da web, como XML, web semântica e serviços da web e projetos de software livre como Akara, uma plataforma de software livre para aplicativos de dados da web. É engenheiro da computação e escritor nascido na Nigéria que mora e trabalha perto de Boulder, Colorado, EUA. Você encontra mais informações sobre o Sr. Ogbuji no seu blog, Copia.