Python 叠代器 & __iter__方法
轉載自 http://blog.csdn.net/bluebird_237/article/details/38894617
叠代器就是重復地做一些事情,可以簡單的理解為循環,在python中實現了__iter__方法的對象是可叠代的,實現了next()方法的對象是叠代器,這樣說起來有點拗口,實際上要想讓一個叠代器工作,至少要實現__iter__方法和next方法。很多時候使用叠代器完成的工作使用列表也可以完成,但是如果有很多值列表就會占用太多的內存,而且使用叠代器也讓我們的程序更加通用、優雅、pythonic。
如果一個類想被用於for ... in
循環,類似list或tuple那樣,就必須實現一個__iter__()
next()
方法拿到循環的下一個值,直到遇到StopIteration錯誤時退出循環。
我們以斐波那契數列為例,寫一個Fib類,可以作用於for循環:
class Fib(object): def __init__(self): self.a, self.b = 0, 1 # 初始化兩個計數器a,b def __iter__(self): return self # 實例本身就是叠代對象,故返回自己 def next(self): self.a, self.b = self.b, self.a + self.b # 計算下一個值 if self.a > 100000: # 退出循環的條件 raise StopIteration(); return self.a # 返回下一個值
現在,試試把Fib實例作用於for循環:
>>> for n in Fib():
... print n
...
1
1
2
3
5
...
46368
75025
叠代器是一個對象,而生成器是一個函數,叠代器和生成器是python中兩個非常強大的特性,編寫程序時你可以不使用生成器達到同樣的效果,但是生成器讓你的程序更加pythonic。創建生成器非常簡單,只要在函數中加入yield語句即可。函數中每次使用yield產生一個值,函數就返回該值,然後停止執行,等待被激活,被激活後繼續在原來的位置執行。下邊的例子實現了同樣的功能:
[python] view plain copy
- #!/usr/bin/env python
- #coding=utf-8
- def fib():
- a,b = 0,1
- while 1:
- a,b = b,a+b
- yield a
- for f in fib():
- if f < 10000:
- print f
- else:
- break
如何叠代?
根本上說, 叠代器就是有一個 next() 方法的對象, 而不是通過索引來計數. 當你或是一個循環機制(例如 for 語句)需要下一個項時, 調用叠代器的 next() 方法就可以獲得它. 條目全部取出後, 會引發一個 StopIteration 異常, 這並不表示錯誤發生, 只是告訴外部調用者, 叠代完成.
不過, 叠代器也有一些限制. 例如你不能向後移動, 不能回到開始, 也不能復制一個叠代器.如果你要再次(或者是同時)叠代同個對象, 你只能去創建另一個叠代器對象. 不過, 這並不糟糕,因為還有其他的工具來幫助你使用叠代器.
reversed() 內建函數將返回一個反序訪問的叠代器. enumerate() 內建函數同樣也返回叠代器.另外兩個新的內建函數, any() 和 all() , 在 Python 2.5 中新增, 如果叠代器中某個/所有條目的值都為布爾真時,則它們返回值為真. 本章先前部分我們展示了如何在 for 循環中通過索引或是可叠代對象來遍歷條目. 同時 Python 還提供了一整個 itertools 模塊, 它包含各種有用的叠代器.
叠代器工作原理
如果這是一個實際應用程序, 那麽我們需要把代碼放在一個 try-except 塊中. 序列現在會自
動地產生它們自己的叠代器, 所以一個 for 循環:
- for i in seq:
- do_something_to(i)
實際上是這樣工作的:
[python] view plain copy- fetch = iter(seq)
- while True:
- try:
- i = fetch.next()
- except StopIteration:
- break
- do_something_to(i)
另外, Python 還引進了三個新的內建字典方法來定義叠代: myDict.iterkeys() (通過 keys 叠
代), myDict.itervalues() (通過 values 叠代), 以及 myDicit.iteritems() (通過 key/value 對來叠代). 註意, in 操作符也可以用於檢查字典的 key 是否存在 , 之前的布爾表達式myDict.has_key(anyKey) 可以被簡寫為 anyKey in myDict .
===文件===
文件對象生成的叠代器會自動調用 readline() 方法. 這樣, 循環就可以訪問文本文件的所有
行. 程序員可以使用 更簡單的 for eachLine in myFile 替換 for eachLine in myFile.readlines() :
- >>>myFile=open(‘config-win.txt’)
- >>> for eachLine in myFile:
- … print eachLine, # comma suppresses extra n
- …
- [EditorWindow]
- font-name: courier new
- font-size: 10
- >>> myFile.close()
可變對象和叠代器
記住,在叠代可變對象的時候修改它們並不是個好主意. 這在叠代器出現之前就是一個問題.
一個流行的例子就是循環列表的時候刪除滿足(或不滿足)特定條件的項:
- for eachURL in allURLs:
- if not eachURL.startswith(‘http://’):
- allURLs.remove(eachURL) # YIKES!!
除列表外的其他序列都是不可變的, 所以危險就發生在這裏. 一個序列的叠代器只是記錄你當前到達第多少個元素, 所以如果你在叠代時改變了元素, 更新會立即反映到你所叠代的條目上.在叠代字典的 key 時, 你絕對不能改變這個字典. 使用字典的 keys() 方法是可以的, 因為keys() 返回一個獨立於字典的列表. 而叠代器是與實際對象綁定在一起的, 它將不會繼續執行下去:
[python] view plain copy- >>> myDict = {‘a’: 1, ‘b’: 2, ‘c’: 3, ‘d’: 4}
- >>> for eachKey in myDict:
- … print eachKey, myDict[eachKey]
- … del myDict[eachKey]
- … a 1
- Traceback (most recent call last):
- File “<stdin>”, line 1, in <module>
- RuntimeError: dictionary changed size during iteration
這樣可以避免有缺陷的代碼. 更多有關叠代器的細節請參閱 PEP 234 .
如何創建叠代器
對一個對象調用 iter() 就可以得到它的叠代器. 它的語法如下:
[python] view plain copy- iter(obj)
- iter(func, sentinel)
如果你傳遞一個參數給 iter() , 它會檢查你傳遞的是不是一個序列, 如果是, 那麽很簡單:
根據索引從 0 一直叠代到序列結束. 另一個創建叠代器的方法是使用類, 我們將在第 13 章詳細
介紹, 一個實現了 __iter__() 和 next() 方法的類可以作為叠代器使用.
如果是傳遞兩個參數給 iter() , 它會重復地調用 func , 直到叠代器的下個值等於sentinel .
Python 叠代器 & __iter__方法