跟一些比較牛X的程序員交流,經常聽到他們嘴里冒出一個不標準的英文單詞,而loop、iterate、traversal和recursion如果不在其內,總覺得他還不夠牛X。當讓,真正牛X的絕對不會這么說的,他們只是說“循環、迭代、遍歷、遞歸”,然后再問“這個你懂嗎?”。哦,這就是真正牛X的程序員。不過,他也僅僅是牛X罷了,還不是大神。大神程序員是什么樣兒呢?他是掃地僧,大隱隱于市。
先搞清楚這些名詞再說別的:
* 循環(loop),指的是在滿足條件的情況下,重復執行同一段代碼。比如,while語句。
* 迭代(iterate),指的是按照某種順序逐個訪問列表中的每一項。比如,for語句。
* 遞歸(recursion),指的是一個函數不斷調用自身的行為。比如,以編程方式輸出著名的斐波納契數列。
* 遍歷(traversal),指的是按照一定的規則訪問樹形結構中的每個節點,而且每個節點都只訪問一次。
對于這四個聽起來高深莫測的詞匯,其實前面,已經涉及到了一個——循環(loop),本節主要介紹一下迭代(iterate),看官在網上google,就會發現,對于迭代和循環、遞歸之間的比較的文章不少,分別從不同角度將它們進行了對比。這里暫不比較,先搞明白python中的迭代。
當然,迭代的話題如果要說起來,會很長,本著循序漸進的原則,這里介紹比較初級的。
## [](https://github.com/qiwsir/StarterLearningPython/blob/master/128.md#逐個訪問)逐個訪問
在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
~~~
`iter()`是一個內建函數,其含義是:
上面的`next()`就是要獲得下一個元素,但是做為一名優秀的程序員,最佳品質就是“懶惰”,當然不能這樣一個一個地敲啦,于是就:
~~~
>>> 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之外,還有別的可以稱作迭代工具。
那么,剛才介紹的iter()的功能呢?它與next()配合使用,也是實現上述迭代工具的作用。
在python中,甚至在其它的語言中,迭代這塊的說法比較亂,主要是名詞亂,剛才我們說,那些能夠實現迭代的東西,稱之為迭代工具,就是這些迭代工具,不少程序員都喜歡叫做迭代器。當然,這都是漢語翻譯,英語就是iterator。
看官看上面的所有例子會發現,如果用for來迭代,當到末尾的時候,就自動結束了,不會報錯。如果用iter()...next()迭代,當最后一個完成之后,它不會自動結束,還要向下繼續,但是后面沒有元素了,于是就報一個稱之為StopIteration的錯誤(這個錯誤的名字叫做:停止迭代,這哪里是報錯,分明是警告)。
看官還要關注iter()...next()迭代的一個特點。當迭代對象lst_iter被迭代結束,即每個元素都讀取了一遍之后,指針就移動到了最后一個元素的后面。如果再訪問,指針并沒有自動返回到首位置,而是仍然停留在末位置,所以報StopIteration,想要再開始,需要重新載入迭代對象。所以,當我在上面重新進行迭代對象賦值之后,又可以繼續了。這在for等類型的迭代工具中是沒有的。
## [](https://github.com/qiwsir/StarterLearningPython/blob/master/128.md#文件迭代器)文件迭代器
現在有一個文件,名稱: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安裝
- 集成開發環境
- 第壹章 基本數據類型
- 數和四則運算
- 除法
- 常用數學函數和運算優先級
- 寫一個簡單的程序
- 字符串(1)
- 字符串(2)
- 字符串(3)
- 字符串(4)
- 字符編碼
- 列表(1)
- 列表(2)
- 列表(3)
- 回顧list和str
- 元組
- 字典(1)
- 字典(2)
- 集合(1)
- 集合(2)
- 第貳章 語句和文件
- 運算符
- 語句(1)
- 語句(2)
- 語句(3)
- 語句(4)
- 語句(5)
- 文件(1)
- 文件(2)
- 迭代
- 練習
- 自省
- 第叁章 函數
- 函數(1)
- 函數(2)
- 函數(3)
- 函數(4)
- 函數練習
- 第肆章 類
- 類(1)
- 類(2)
- 類(3)
- 類(4)
- 類(5)
- 多態和封裝
- 特殊方法(1)
- 特殊方法(2)
- 迭代器
- 生成器
- 上下文管理器
- 第伍章 錯誤和異常
- 錯誤和異常(1)
- 錯誤和異常(2)
- 錯誤和異常(3)
- 第陸章 模塊
- 編寫模塊
- 標準庫(1)
- 標準庫(2)
- 標準庫(3)
- 標準庫(4)
- 標準庫(5)
- 標準庫(6)
- 標準庫(7)
- 標準庫(8)
- 第三方庫
- 第柒章 保存數據
- 將數據存入文件
- mysql數據庫(1)
- MySQL數據庫(2)
- mongodb數據庫(1)
- SQLite數據庫
- 電子表格
- 第捌章 用Tornado做網站
- 為做網站而準備
- 分析Hello
- 用tornado做網站(1)
- 用tornado做網站(2)
- 用tornado做網站(3)
- 用tornado做網站(4)
- 用tornado做網站(5)
- 用tornado做網站(6)
- 用tornado做網站(7)
- 第玖章 科學計算
- 為計算做準備
- Pandas使用(1)
- Pandas使用(2)
- 處理股票數據
- 附:網絡文摘
- 如何成為Python高手
- ASCII、Unicode、GBK和UTF-8字符編碼的區別聯系