<Dive into Python>大补贴(1)
<Dive into Python>大补贴
作者:赖勇浩
前言
前几日发了一篇<为什么<Dive into Python>不值得推荐>的贴子,有朋友指出这本书虽然不适合初学者,但里面的一些内容还是可以当作提高篇的,最大的问题也许在于它基于老版本的 Python,并提出给 DIP 打补丁的想法。我觉得打补丁的想法还是很有意义的,而我也正好想宣传一下 Python 的新特性,所以决定写这 DIP 大补贴这样的系列文章,狗尾续貂,还请大家多多指教。
这一份补丁基于官网中文版5.4b(http://www.woodpecker.org.cn/diveintopython)的第二章至第六章,更新 DIP 的内容到适合 Python2.5 版本,涉及的特性有较大一部分必须2.5版本才能应用。
本文假定读者已经掌握 Python 的语法和常用模块,完全不懂 Python 者不是作者的预期读者,请先读 Tutorial。
另,本文会提到 Python3.0 版本的一些变化,但不会深入讨论 3.0 特性,只是告诉大家这东西已经变了样儿啦。想在这方面了解更多,请阅读 Python3.0 的在线文档。
第一贴
在第2章第一节(http://www.woodpecker.org.cn/diveintopython/getting_to_know_python/index.html)的例2.1 更新以后的代码为:
def buildConnectionString(params):
"""Build a connection string from a dictionary of parameters.
Returns string."""
return ";".join("%s=%s" % (k, v) for k, v in params.iteritems())
if __name__ == "__main__":
myParams = {"server":"mpilgrim",
"database":"master",
"uid":"sa",
"pwd":"secret"
}
print buildConnectionString(myParams)
改变有三个:
一、去除了定义 myParams 时的续行符;
二、去除了 join() 实参中的临时列表,使用生成器表达式;
三、字典的 items() 函数改为 iteritems()。
下面具体来讲讲这三个新特性。
续行规则
Python 的续行规则分为两种:显式续行和隐式续行。显式即 DIP 中的在物理行结束符前加续行符表示下一行与本行是处于同一逻辑行。显式有两个缺点,一是加入续行符让代码看起来比较丑陋,不够 Pythonic;二是续行符后面必须是换行符,无法在同一行添加行注释。
隐式续行完美地解决了这两个问题,除了一个限制:隐式换行只可以出现在圆括号、方括号和花括号之间。简单来说,就是我们只有在定义函数、调用函数、定义元组、列表和字典时使用隐式续行,如:
>>> def foo(a, # 这是一个行注释,显式续行做不到哦~
... b):
... print a, b
...
>>> foo(100,
... 10)
100 10
>>> atuple = (1, 2, 3,
... 4, 5, 6)
>>> alist = [1, 2, 3,
... 4, 5, 6]
>>> adict = {'abc' : 1,
... 'def' : 2}
隐式续行在 from ... import ... 语句中也能发挥重要作用:
>>> from os.path import (isfile, isdir,
... islink, ismount)
生成器表达式(Genetaor Expression)
原文使用的”["%s=%s" % (k, v) for k, v in params.items()]”称为list comprehension(译为列表包含或列表理解的都有,因为统一,本文使用英文),改为生成器表达式是为了提升性能,优化内存的使用。生成器表达式是用来生成函数调用时序列参数的一种迭代器写法,在语法上跟 list comprehension 差不多,不过把方括号换成了圆括号,如:
>>> i = (i for i in xrange(10))
大家千万别以为 i 是一个元组,其实它是一个生成器对象:
>>> i
<generator object at 0x830b
生成器对象可以遍历或转化为列表(或元组等数据结构),但不能切片(slicing)。当函数的唯一的实参是可迭代序列时,便可以去掉生成器表达式两端的圆括号,写出更优雅的代码:
>>> sum(i for i in xrange(10))
45
需要注意的是函数接受多个实参时,必须加上圆括号:
>>> map(str, (i for i in xrange(10)))
['0', '1', '2', '3', '4', '5', '6', '7', '8', '9']
dict 的迭代遍历
随着 Python 的发展,迭代器理念在社区中深得人心,语言也源源不断地增加新特性来支持它。作为最常用的数据结构之一,dict 提供内置的迭代遍历方法是必然的。调用dict.items()函数族(包括dict.keys()和dict.values())都返回实实在在的列表对象,这在遍历元素众多的 dict 对象时往往会引起垃圾收集机制动行,导致性能下降。
现代 Python 改造了 dict,为它增加了 iteritems() 函数族(包括 dict.iterkeys() 和 dict.itervalues())。它们的返回值是一个迭代器对象,而非占用大量内存的列表对象,这一特性使得基于 dict 的迭代更加高效。
类似的机制还有 xrange 对象、file.xreadlines() 等。值得一提的是 dict 和 file 提供更简洁的方式进行迭代遍历:
>>> for key in adict:
... do_something_with(key)
>>> for line in file_obj:
... do_something_with(line)
这里的 adict、file_obj 与 adict.iterkeys()、file_obj.xreadlines()有相同的效果,但显得更为 Pythonic,是推荐写法。
Python3.0
dict 的 iteritems() 函数族也好,xrange()也罢,在 Py3.0 里都改头换面了。Py3.0 是更彻底的基于迭代器编程的语言,dict 的 items() 函数族直接返回迭代器,就是说它的行为跟 Py2.x 的 iteritems() 函数族是一样的;而 iteritems() 函数族则直接“消失”了。类似的还有 range() 返回生成器,file.readlines() 返回迭代器;去除了 xrange() 内置函数和 file.xreadlines() 方法。