在Python中,不少對象都是能夠經過for語句來直接遍歷的,例如list、string、dict等等,這些對象均可以被稱爲可迭代對象。至於說哪些對象是能夠被迭代訪問的,就要了解一下迭代器相關的知識了。python
迭代器對象要求支持迭代器協議的對象,在Python中,支持迭代器協議就是實現對象的__iter__()和next()方法。其中__iter__()方法返回迭代器對象自己;next()方法返回容器的下一個元素,在結尾時引起StopIteration異常。函數
這兩個方法是迭代器最基本的方法,一個用來得到迭代器對象,一個用來獲取容器中的下一個元素。lua
對於可迭代對象,可使用內建函數iter()來獲取它的迭代器對象:spa
例子中,經過iter()方法得到了list的迭代器對象,而後就能夠經過next()方法來訪問list中的元素了。當容器中沒有可訪問的元素後,next()方法將會拋出一個StopIteration異常終止迭代器。3d
其實,當咱們使用for語句的時候,for語句就會自動的經過__iter__()方法來得到迭代器對象,而且經過next()方法來獲取下一個元素。code
瞭解了迭代器協議以後,就能夠自定義迭代器了。對象
下面例子中實現了一個MyRange的類型,這個類型中實現了__iter__()方法,經過這個方法返回對象自己做爲迭代器對象;同時,實現了next()方法用來獲取容器中的下一個元素,當沒有可訪問元素後,就拋出StopIteration異常。blog
class MyRange(object): def __init__(self, n): self.idx = 0 self.n = n def __iter__(self): return self def next(self): if self.idx < self.n: val = self.idx self.idx += 1 return val else: raise StopIteration()
這個自定義類型跟內建函數xrange很相似,看一下運行結果:遞歸
myRange = MyRange(3) for i in myRange: print i
在上面的例子中,myRange這個對象就是一個可迭代對象,同時它自己也是一個迭代器對象。內存
看下面的代碼,對於一個可迭代對象,若是它自己又是一個迭代器對象,就會有下面的 問題,就沒有辦法支持屢次迭代。
爲了解決上面的問題,能夠分別定義可迭代類型對象和迭代器類型對象;而後可迭代類型對象的__iter__()方法能夠得到一個迭代器類型的對象。看下面的實現:
class Zrange: def __init__(self, n): self.n = n def __iter__(self): return ZrangeIterator(self.n) class ZrangeIterator: def __init__(self, n): self.i = 0 self.n = n def __iter__(self): return self def next(self): if self.i < self.n: i = self.i self.i += 1 return i else: raise StopIteration() zrange = Zrange(3) print zrange is iter(zrange) print [i for i in zrange] print [i for i in zrange]
代碼的運行結果爲:
其實,經過下面代碼能夠看出,list類型也是按照上面的方式,list自己是一個可迭代對象,經過iter()方法能夠得到list的迭代器對象:
在Python中,使用生成器能夠很方便的支持迭代器協議。生成器經過生成器函數產生,生成器函數能夠經過常規的def語句來定義,可是不用return返回,而是用yield一次返回一個結果,在每一個結果之間掛起和繼續它們的狀態,來自動實現迭代協議。
也就是說,yield是一個語法糖,內部實現支持了迭代器協議,同時yield內部是一個狀態機,維護着掛起和繼續的狀態。
下面看看生成器的使用:
在這個例子中,定義了一個生成器函數,函數返回一個生成器對象,而後就能夠經過for語句進行迭代訪問了。
其實,生成器函數返回生成器的迭代器。 "生成器的迭代器"這個術語一般被稱做"生成器"。要注意的是生成器就是一類特殊的迭代器。做爲一個迭代器,生成器必需要定義一些方法,其中一個就是next()。如同迭代器同樣,咱們可使用next()函數來獲取下一個值。
下面就仔細看看生成器是怎麼工做的。
從上面的例子也能夠看到,生成器函數跟普通的函數是有很大差異的。
結合上面的例子咱們加入一些打印信息,進一步看看生成器的執行流程:
經過結果能夠看到:
在開始介紹生成器表達式以前,先看看咱們比較熟悉的列表解析( List comprehensions),列表解析通常都是下面的形式。
[expr for iter_var in iterable if cond_expr]
迭代iterable裏全部內容,每一次迭代後,把iterable裏知足cond_expr條件的內容放到iter_var中,再在表達式expr中應該iter_var的內容,最後用表達式的計算值生成一個列表。
例如,生成一個list來保護50之內的因此奇數:
[i for i in range(50) if i%2]
生成器表達式是在python2.4中引入的,當序列過長, 而每次只須要獲取一個元素時,應當考慮使用生成器表達式而不是列表解析。生成器表達式的語法和列表解析同樣,只不過生成器表達式是被()括起來的,而不是[],以下:
(expr for iter_var in iterable if cond_expr)
看一個例子:
生成器表達式並非建立一個列表, 而是返回一個生成器,這個生成器在每次計算出一個條目後,把這個條目"產生"(yield)出來。 生成器表達式使用了"惰性計算"(lazy evaluation),只有在檢索時才被賦值(evaluated),因此在列表比較長的狀況下使用內存上更有效。
繼續看一個例子:
從這個例子中能夠看到,生成器表達式產生的生成器,它自身是一個可迭代對象,同時也是迭代器自己。
生成器能夠向函數同樣進行遞歸使用的,下面看一個簡單的例子,對一個序列進行全排列:
def permutations(li): if len(li) == 0: yield li else: for i in range(len(li)): li[0], li[i] = li[i], li[0] for item in permutations(li[1:]): yield [li[0]] + item for item in permutations(range(3)): print item
代碼的結果爲:
生成器中還有兩個很重要的方法:send()和close()。
從前面瞭解到,next()方法能夠恢復生成器狀態並繼續執行,其實send()是除next()外另外一個恢復生成器的方法。
Python 2.5中,yield語句變成了yield表達式,也就是說yield能夠有一個值,而這個值就是send()方法的參數,因此send(None)和next()是等效的。一樣,next()和send()的返回值都是yield語句處的參數(yielded value)
關於send()方法須要注意的是:調用send傳入非None值前,生成器必須處於掛起狀態,不然將拋出異常。也就是說,第一次調用時,要使用next()語句或send(None),由於沒有yield語句來接收這個值。
這個方法用於關閉生成器,對關閉的生成器後再次調用next或send將拋出StopIteration異常。
下面看看這兩個方法的使用:
本文介紹了Python迭代器和生成器的相關內容。