跟一些比較牛X的程序員交流,經常聽到他們嘴里冒出一個不標準的英文單詞,而loop、iterate、traversal和recursion如果不在其內,總覺得他還不夠牛X。當讓,真正牛X的絕對不會這么說的,他們只是說“循環、迭代、遍歷、遞歸”,然后再問“這個你懂嗎?”。哦,這就是真正牛X的程序員。不過,他也僅僅是牛X罷了,還不是大神。大神程序員是什么樣兒呢?他是掃地僧,大隱隱于市。
先搞清楚這些名詞再說別的:
* 循環(loop),指的是在滿足條件的情況下,重復執行同一段代碼。比如,while語句。
* 迭代(iterate),指的是按照某種順序逐個訪問列表中的每一項。比如,for語句。
* 遞歸(recursion),指的是一個函數不斷調用自身的行為。比如,以編程方式輸出著名的斐波納契數列。
* 遍歷(traversal),指的是按照一定的規則訪問樹形結構中的每個節點,而且每個節點都只訪問一次。
對于這四個聽起來高深莫測的詞匯,在教程中,已經涉及到了一個——循環(loop),本經主要介紹一下迭代(iterate),看官在網上google,就會發現,對于迭代和循環、遞歸之間的比較的文章不少,分別從不同角度將它們進行了對比。這里暫不比較,先搞明白python中的迭代。之后適當時機再比較,如果我不忘記的話,哈哈。
## 逐個訪問
在python中,訪問對象中每個元素,可以這么做:(例如一個list)
~~~
>>> lst
['q', 'i', 'w', 's', 'i', 'r']
>>> for i in lst:
... print i,
...
q i w s i r
~~~
除了這種方法,還可以這樣:
~~~
>>> lst_iter = iter(lst) #對原來的list實施了一個iter()
>>> lst_iter.next() #要不厭其煩地一個一個手動訪問
'q'
>>> lst_iter.next()
'i'
>>> lst_iter.next()
'w'
>>> lst_iter.next()
's'
>>> lst_iter.next()
'i'
>>> lst_iter.next()
'r'
>>> lst_iter.next()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
StopIteration
~~~
做為一名優秀的程序員,最佳品質就是“懶惰”,當然不能這樣一個一個地敲啦,于是就:
~~~
>>> while True:
... print lst_iter.next()
...
Traceback (most recent call last): #居然報錯,而且錯誤跟前面一樣?什么原因
File "<stdin>", line 2, in <module>
StopIteration
>>> lst_iter = iter(lst) #那就再寫一遍,上面的錯誤暫且擱置,回頭在研究
>>> while True:
... print lst_iter.next()
...
q #果然自動化地讀取了
i
w
s
i
r
Traceback (most recent call last): #讀取到最后一個之后,報錯,停止循環
File "<stdin>", line 2, in <module>
StopIteration
>>>
~~~
首先了解一下上面用到的那個內置函數:iter(),官方文檔中有這樣一段話描述之:
> iter(o[, sentinel])
>
> Return an iterator object. The first argument is interpreted very differently depending on the presence of the second argument. Without a second argument, o must be a collection object which supports the iteration protocol (the?**iter**() method), or it must support the sequence protocol (the?**getitem**() method with integer arguments starting at 0). If it does not support either of those protocols, TypeError is raised. If the second argument, sentinel, is given, then o must be a callable object. The iterator created in this case will call o with no arguments for each call to its next() method; if the value returned is equal to sentinel, StopIteration will be raised, otherwise the value will be returned.
大意是說...(此處故意省略若干字,因為我相信看此文章的看官英語水平是達到看文檔的水平了,如果沒有,也不用著急,找個詞典什么的幫助一下。)
盡管不翻譯了,但是還要提煉一下主要的東西:
* 返回值是一個迭代器對象
* 參數需要是一個符合迭代協議的對象或者是一個序列對象
* next()配合與之使用
什么是“可迭代的對象”呢?一般,我們常常將哪些能夠用for來一個一個讀取元素的對象,就稱之為可迭代的對象。那么for也就被稱之為迭代工具。所謂迭代工具,就是能夠按照一定順序掃描迭代對象的每個元素(按照從左到右的順序),顯然,除了for之外,還有別的可以稱作迭代工具,比如列表解析,in來判斷某元素是否屬于序列對象等。
那么,剛才介紹的iter()的功能呢?它與next()配合使用,也是實現上述迭代工具的作用。在python中,甚至在其它的語言中,迭代這塊的說法比較亂,主要是名詞亂,剛才我們說,那些能夠實現迭代的東西,稱之為迭代工具,就是這些迭代工具,不少程序員都喜歡叫做迭代器。當然,這都是漢語翻譯,英語就是iterator。
看官看上面的所有例子會發現,如果用for來迭代,當到末尾的時候,就自動結束了,不會報錯。如果用iter()...next()迭代,當最后一個完成之后,它不會自動結束,還要向下繼續,但是后面沒有元素了,于是就報一個稱之為StopIteration的錯誤(這個錯誤的名字叫做:停止迭代,這哪里是報錯,分明是警告)。
看官還要關注iter()...next()迭代的一個特點。當迭代對象lst_iter被迭代結束,即每個元素都讀取一邊之后,指針就移動到了最后一個元素的后面。如果再訪問,指針并沒有自動返回到首位置,而是仍然停留在末位置,所以報StopIteration,想要再開始,需要重新再入迭代對象。所以,列位就看到,當我在上面重新進行迭代對象賦值之后,又可以繼續了。這在for等類型的迭代工具中是沒有的。
## 文件迭代器
現在有一個文件,名稱:208.txt,其內容如下:
~~~
Learn python with qiwsir.
There is free python course.
The website is:
http://qiwsir.github.io
Its language is Chinese.
~~~
用迭代器來操作這個文件,我們在前面講述文件有關知識的時候已經做過了,無非就是:
~~~
>>> f = open("208.txt")
>>> f.readline() #讀第一行
'Learn python with qiwsir.\n'
>>> f.readline() #讀第二行
'There is free python course.\n'
>>> f.readline() #讀第三行
'The website is:\n'
>>> f.readline() #讀第四行
'http://qiwsir.github.io\n'
>>> f.readline() #讀第五行,也就是這真在讀完最后一行之后,到了此行的后面
'Its language is Chinese.\n'
>>> f.readline() #無內容了,但是不報錯,返回空。
''
~~~
以上演示的是用readline()一行一行地讀。當然,在實際操作中,我們是絕對不能這樣做的,一定要讓它自動進行,比較常用的方法是:
~~~
>>> for line in f: #這個操作是緊接著上面的操作進行的,請看官主要觀察
... print line, #沒有打印出任何東西
...
~~~
這段代碼之所沒有打印出東西來,是因為經過前面的迭代,指針已經移到了最后了。這就是迭代的一個特點,要小心指針的位置。
~~~
>>> f = open("208.txt") #從頭再來
>>> for line in f:
... print line,
...
Learn python with qiwsir.
There is free python course.
The website is:
http://qiwsir.github.io
Its language is Chinese.
~~~
這種方法是讀取文件常用的。另外一個readlines()也可以。但是,需要有一些小心的地方,看官如果想不起來小心什么,可以在將關于文件的課程復習一邊。
上面過程用next()也能夠讀取。
~~~
>>> f = open("208.txt")
>>> f.next()
'Learn python with qiwsir.\n'
>>> f.next()
'There is free python course.\n'
>>> f.next()
'The website is:\n'
>>> f.next()
'http://qiwsir.github.io\n'
>>> f.next()
'Its language is Chinese.\n'
>>> f.next()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
StopIteration
~~~
如果用next(),就可以直接讀取每行的內容。這說明文件是天然的可迭代對象,不需要用iter()轉換了。
再有,我們用for來實現迭代,在本質上,就是自動調用next(),只不過這個工作,已經讓for偷偷地替我們干了,到這里,列位是不是應該給for取另外一個名字:它叫雷鋒。
前面提到了,列表解析也能夠做為迭代工具,在研究列表的時候,看官想必已經清楚了。那么對文件,是否可以用?試一試:
~~~
>>> [ line for line in open('208.txt') ]
['Learn python with qiwsir.\n', 'There is free python course.\n', 'The website is:\n', 'http://qiwsir.github.io\n', 'Its language is Chinese.\n']
~~~
至此,看官難道還不為列表解析所折服嗎?真的很強大,又強又大呀。
其實,迭代器遠遠不止上述這么簡單,下面我們隨便列舉一些,在python中還可以這樣得到迭代對象中的元素。
~~~
>>> list(open('208.txt'))
['Learn python with qiwsir.\n', 'There is free python course.\n', 'The website is:\n', 'http://qiwsir.github.io\n', 'Its language is Chinese.\n']
>>> tuple(open('208.txt'))
('Learn python with qiwsir.\n', 'There is free python course.\n', 'The website is:\n', 'http://qiwsir.github.io\n', 'Its language is Chinese.\n')
>>> "$$$".join(open('208.txt'))
'Learn python with qiwsir.\n$$$There is free python course.\n$$$The website is:\n$$$http://qiwsir.github.io\n$$$Its language is Chinese.\n'
>>> a,b,c,d,e = open("208.txt")
>>> a
'Learn python with qiwsir.\n'
>>> b
'There is free python course.\n'
>>> c
'The website is:\n'
>>> d
'http://qiwsir.github.io\n'
>>> e
'Its language is Chinese.\n'
~~~
上述方式,在編程實踐中不一定用得上,只是向看官展示一下,并且看官要明白,可以這么做,不是非要這么做。
補充一下,字典也可以迭代,看官自己不妨摸索一下(其實前面已經用for迭代過了,這次請摸索一下用iter()...next()手動一步一步迭代)。
- 第零部分 獨上高樓,望盡天涯路
- 嘮叨一些關于Python的事情
- 為什么要開設本欄目
- 第一部分 積小流,至江海
- Python環境安裝
- 集成開發環境(IDE)
- 數的類型和四則運算
- 啰嗦的除法
- 開始真正編程
- 初識永遠強大的函數
- 玩轉字符串(1):基本概念、字符轉義、字符串連接、變量與字符串關系
- 玩轉字符串(2)
- 玩轉字符串(3)
- 眼花繚亂的運算符
- 從if開始語句的征程
- 一個免費的實驗室
- 有容乃大的list(1)
- 有容乃大的list(2)
- 有容乃大的list(3)
- 有容乃大的list(4)
- list和str比較
- 畫圈還不簡單嗎
- 再深點,更懂list
- 字典,你還記得嗎?
- 字典的操作方法
- 有點簡約的元組
- 一二三,集合了
- 集合的關系
- Python數據類型總結
- 深入變量和引用對象
- 賦值,簡單也不簡單
- 坑爹的字符編碼
- 做一個小游戲
- 不要紅頭文件(1): open, write, close
- 不要紅頭文件(2): os.stat, closed, mode, read, readlines, readline
- 第二部分 窮千里目,上一層樓
- 正規地說一句話
- print能干的事情
- 從格式化表達式到方法
- 復習if語句
- 用while來循環
- 難以想象的for
- 關于循環的小伎倆
- 讓人歡喜讓人憂的迭代
- 大話題小函數(1)
- 大話題小函數(2)
- python文檔
- 重回函數
- 變量和參數
- 總結參數的傳遞
- 傳說中的函數條規
- 關于類的基本認識
- 編寫類之一創建實例
- 編寫類之二方法
- 編寫類之三子類
- 編寫類之四再論繼承
- 命名空間
- 類的細節
- Import 模塊
- 模塊的加載
- 私有和專有
- 折騰一下目錄: os.path.<attribute>
- 第三部分 昨夜西風,亭臺誰登
- 網站的結構:網站組成、MySQL數據庫的安裝和配置、MySQL的運行
- 通過Python連接數據庫:安裝python-MySQLdb,連接MySQL
- 用Pyton操作數據庫(1):建立連接和游標,并insert and commit
- 用Python操作數據庫(2)
- 用Python操作數據庫(3)
- python開發框架:框架介紹、Tornado安裝
- Hello,第一個網頁分析:tornado網站的基本結構剖析:improt模塊、RequestHandler, HTTPServer, Application, IOLoop
- 實例分析get和post:get()通過URL得到數據和post()通過get_argument()獲取數據
- 問候世界:利用GAE建立tornado框架網站
- 使用表單和模板:tornado模板self.render和模板變量傳遞
- 模板中的語法:tornado模板中的for,if,set等語法
- 靜態文件以及一個項目框架
- 模板轉義
- 第四部分 暮然回首,燈火闌珊處
- requests庫
- 比較json/dictionary的庫
- defaultdict 模塊和 namedtuple 模塊
- 第五部分 Python備忘錄
- 基本的(字面量)值
- 運算符
- 常用的內建函數
- 擴展閱讀(來自網絡文章)
- 人生苦短,我用Python