(点击
上方公众号
,可快速关注)
来源:Uche Ogbuji
链接:www.ibm.com/developerworks/cn/opensource/os-pypy-intro/
概述
Python 编程语言于 1994 年问世,自新千年以来,这种语言获得了极大的成功。衡量一种语言成功与否的标准之一就是其实现的数量。最知名也是最常用的 Python 实现称为 CPython。此外还有其他一些成功的项目,例如 Jython(在 Java ™ 运行时中工作的 Python 语言)和 IronPython(在 .NET 平台上工作的 Python 语言)。所有这些项目都是开放源码的,而 Python 在开放源码软件世界中始终有着极大的影响力。
Python 实现的一个由来已久的目标就是支持纯语言设计,通过以自己的方式指定相关语言来 “引导” Python 的定义,而不是按照 C 和 Java 等其他语言的方式做出规定。PyPy 项目正是应此需求而出现的一种 Python 实现。PyPy 表示 “用 Python 实现的 Python”,但实际上它是使用一个称为 RPython 的 Python 子集实现的。更准确地来说,PyPy 自身就是一种运行时,您可以在其中插入任何语言。
PyPy 整洁的语言设计使之非常适合嵌入低级优化器,提供诸多优化优势。具体来说,PyPy 集成了一种即时 (JIT) 编译器。这与能够以革命性的方式改变 Java 性能的知名技术 HotSpot 属于同一种技术的不同形式,Sun Microsystems 于 21 世纪初期从 Animorphic 手中收购了 HotSpot,并整合到了自己的 Java 实现之中,使这种语言适用于大多数用途。Python 原本已经适用于多种用途,但性能是最常被人们抱怨的问题。PyPy 的跟踪 JIT 编译器已经展现了它革新 Python 程序性能的能力,尽管我认为这个项目仍然处于后续测试阶段,但它已经是 Python 程序员的一种重要工具,是开发人员工具箱的有用补充。
在这篇文章中,我将介绍 PyPy,而且假设读者并不具备丰富的 Python 背景知识。
入门
首先,请不要将 PyPy 与 PyPI 混淆。这是两个截然不同的项目。PyPI 即 Python Package Index,是获得第三方 Python 软件包以补充标准库的一个站点及系统。在您进入正确的 PyPy 站点之后(请参见 参考资料 部分),您会看到开发人员已经使大多数用户能够轻松开始尝试使用 PyPy。如果您在最新的硬件上使用 Linux®、Mac 或 Windows®(不含 Windows 64,目前尚不支持 Windows 64),那么就应该能够直接下载并执行一个二进制软件包。
PyPY 的最新版本是 1.8,它充分实现了 Python 2.7.2,也就是说能够兼容这个版本的 CPython 的语言特性和行为。然而,在许多基准使用当中,它的速度已经远远超过了 CPython 2.7.2,这是它引起我们注意的真正因素。下面的会话展示了我在 Ubuntu 11.04 机器上安装 PyPy 的过程。这段会话来自旧版本的 PyPy,但 PyPy 1.8 也会提供类似的结果。
$
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
/
现在,您需要更新 $PATH,以包含 ~/.local/bin/。安装 PyPy 之后,建议您同样安装 Distribute 和 Pip,以便简化额外软件包的安装。(尽管本文中未提及,但您也可能需要使用 Virtualenv,这是保持独立、整洁的 Python 环境的一种方法。)以下会话展示了 Distribute 和 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
您应发现,库文件安装在 ~/.local/pypy-1.8/site-packages/ 目录之中,可执行文件位于 ~/.local/pypy-1.8/bin 目录之中,因此您可能希望将后者添加到 $PATH。此外,务必确保使用了之前安装的 pip,而不是系统级的 pip。随后,您就可以安装本文稍后要用到的第三方软件包。
$ pip install html5lib $ pip install pyparsing
清单 1 展示了调用 Python 的 “彩蛋” import this 之后,PyPy 解释器的输出结果。
清单 1. 示例 PyPy 输出
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
!
>>>>
链接统计
作为 PyPy 的一个简单实例,我将展示一个解析网页并打印出网页中表示的链接列表的程序。这正是网络蜘蛛 (spidering) 软件的基本理念,即出于某些目的跟踪页面间的链接网络。
在解析方面,我选择了 html5lib,这是一种纯 Python 解析库,设计用于实现定义了 HTML5 规范的 WHAT-WG 组织的解析算法。HTML5 针对向后兼容性而设计,即便可以兼容损坏的网页。因此 html5lib 同时也是一种出色的通用 HTML 解析工具包。这种工具在 CPython 和 PyPy 上执行了基准测试,在 PyPy 上的速度明显要更快。
清单 2 解析了一个特定的网页,逐行打印了该页面中的链接。您在命令行中指定目标页面 URL,例如: pypy listing1.py http://www.ibm.com/developerworks/opensource/。
清单 2. 列出一个页面中的链接
#!/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
我在代码中作了非常详尽的注释,因为我并未假设读者拥有深厚的 Python 知识,但您应该了解一些基本知识,例如,如何使用缩排格式来表示控制流。相关的 Python 教程请参见 参考资料 部分。
为简单起见,我避免了此类程序的一些惯例,但确实使用了一项我认为即便对入门级的程序员也非常有用的高级特性。函数 list_links 称为生成器。它是一个像序列一样操作的函数,可依次计算并提供项目。yield 语句是这里的关键,它提供了值序列。
更为复杂的屏幕屏幕抓取
大多数网页解析任务都不仅仅是发现和显示链接那样简单,有一些库可以帮助处理典型的 “网络抓取” 任务。Pyparsing 是一种通用的纯 Python 解析工具包,包含一些支持 HTML 解析的工具。
在下一个示例中,我将展示如何从 IBM developerWorks 索引页面中抓取文章列表。图 1 展示了目标页面的屏幕快照。清单 3 是 HTML 形式的示例记录。
图 1. 需要处理的 IBM developerWorks 网页
清单 3. 待处理的 HTML 示例记录
href
=
"http://www.ibm.com/developerworks/opensource/library/os-wc3jam/index.html"
>
Join the social business revolution
Social media has become social business and everyone from
business leadership to software developers need to understand
the tools and techniques that will be required.
The World Wide Web Consortium (W3C) will be conducting a
social media event to discuss relevant standards and requirement
for the near and far future.
Articles
class
=
"dw-nowrap"
>
03 Nov 2011
清单 4 给出了解析这个页面的代码。同样,我仍然在这里给出了大量注释,但在给出清单后,我还要说明几个重要新概念。
清单 4. 从网页中提取文章列表
#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