標籤:列印 關閉 turn 結果 pytho 簡單 函數返回 for語句 另一個
在Python中,很多個物件都是可以通過for語句來直接遍曆的,例如list、string、dict等等,這些對象都可以被稱為可迭代對象。至於說哪些對象是可以被迭代訪問的,就要瞭解一下迭代器相關的知識了。
迭代器
迭代器對象要求支援迭代器協議的對象,在Python中,支援迭代器協議就是實現對象的__iter__()和next()方法。其中__iter__()方法返回迭代器對象本身;next()方法返回容器的下一個元素,在結尾時引發StopIteration異常。
__iter__()和next()方法
這兩個方法是迭代器最基本的方法,一個用來獲得迭代器對象,一個用來擷取容器中的下一個元素。
對於可迭代對象,可以使用內建函數iter()來擷取它的迭代器對象:
例子中,通過iter()方法獲得了list的迭代器對象,然後就可以通過next()方法來訪問list中的元素了。當容器中沒有可訪問的元素後,next()方法將會拋出一個StopIteration異常終止迭代器。
其實,當我們使用for語句的時候,for語句就會自動的通過__iter__()方法來獲得迭代器對象,並且通過next()方法來擷取下一個元素。
自訂迭代器
瞭解了迭代器協議之後,就可以自訂迭代器了。
下面例子中實現了一個MyRange的類型,這個類型中實現了__iter__()方法,通過這個方法返回對象本身作為迭代器對象;同時,實現了next()方法用來擷取容器中的下一個元素,當沒有可訪問元素後,就拋出StopIteration異常。
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()函數來擷取下一個值。
產生器執行流程
下面就仔細看看產生器是怎麼工作的。
從上面的例子也可以看到,產生器函數跟普通的函數是有很大差別的。
結合上面的例子我們加入一些列印資訊,進一步看看產生器的執行流程:
通過結果可以看到:
- 當調用產生器函數的時候,函數只是返回了一個產生器對象,並沒有 執行。
- 當next()方法第一次被調用的時候,產生器函數才開始執行,執行到yield語句處停止
- next()方法的傳回值就是yield語句處的參數(yielded value)
- 當繼續調用next()方法的時候,函數將接著上一次停止的yield語句處繼續執行,併到下一個yield處停止;如果後面沒有yield就拋出StopIteration異常
產生器運算式
在開始介紹產生器運算式之前,先看看我們比較熟悉的列表解析( 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()方法
產生器中還有兩個很重要的方法:send()和close()。
- send(value):
從前面瞭解到,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語句來接收這個值。
- close():
這個方法用於關閉產生器,對關閉的產生器後再次調用next或send將拋出StopIteration異常。
下面看看這兩個方法的使用:
總結
本文介紹了Python迭代器和產生器的相關內容。
- 通過實現迭代器協議對應的__iter__()和next()方法,可以自訂迭代器類型。對於可迭代對象,for語句可以通過iter()方法擷取迭代器,並且通過next()方法獲得容器的下一個元素。
- 像列表這種序列類型的對象,可迭代對象和迭代器對象是相互獨立存在的,在迭代的過程中各個迭代器相互獨立;但是,有的可迭代對象本身又是迭代器對象,那麼迭代器就沒法獨立使用。
- itertools模組提供了一系列迭代器,能夠協助使用者輕鬆地使用排列、組合、笛卡爾積或其他組合結構。
- 產生器是一種特殊的迭代器,內部支援了產生器協議,不需要明確定義__iter__()和next()方法。
- 產生器通過產生器函數產生,產生器函數可以通過常規的def語句來定義,但是不用return返回,而是用yield一次返回一個結果。
- 出處:https://www.cnblogs.com/wilber2013/p/4652531.html
(轉)python基礎學習-----產生器和迭代器