<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 0x830b32c>
產生器對象可以遍曆或轉化為列表(或元組等資料結構),但不能切片(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() 方法。