Python的迭代器集成在語言之中,迭代器和生成器是Python中很重要的用法,本文將深刻了解迭代器和生成器。python
首先,咱們都知道for循環是一個基礎迭代操做,大多數的容器對象均可以使用for循環,那麼,咱們從for循環開始:git
你有沒有想過,for循環的內部實現原理呢?github
其實,在Python中,for循環是對迭代器進行迭代的語法糖,內部運行機理就是:首先底層對循環對象實現迭代器包裝(調用容器對象的__iter__
方法)返回一個迭代器對象,每循環一步,就調用一次迭代器對象的__next__
方法,直到循環結束時,自動處理StopIteration這個異常。函數
對於像list,dict等容器對象而言,均可以使用for循環,可是它們並非迭代器,它們屬於可迭代對象。性能
什麼是可迭代對象呢?code
最簡單的解釋:實現了迭代方法能夠被迭代的對象,能夠使用isinstance()方法進行判斷。協程
舉個例子:對象
In [1]: from collections import Iterable, Iterator In [2]: a = [1, 2, 3] In [3]: isinstance(a, Iterable) Out[3]: True In [4]: b = a.__iter__() In [5]: isinstance(b, Iterator) Out[5]: True
可迭代對象實現了__iter__
方法,該方法返回一個迭代器對象。ip
以上,能夠看到,在迭代過程當中,實際調用了迭代器的__next__
方法進行迭代。內存
那麼,什麼是迭代器?
實現了迭代器協議的對象就是迭代器,所謂的迭代器協議能夠簡單概括爲:
__iter__()
方法,返回一個迭代器迭代器和可迭代對象的區別是:迭代器能夠使用next()方法不斷調用並返回下一個值,除了調用可迭代對象的__iter__
方法來將可迭代對象轉換爲迭代器之外,還能夠使用iter()方法。
舉個例子來驗證以上說法:
In [1]: iter_data = iter([1, 2, 3]) In [2]: print(next(iter_data)) 1 In [3]: print(next(iter_data)) 2 In [4]: print(next(iter_data)) 3 In [5]: print(next(iter_data)) --------------------------------------------------------------------------- StopIteration Traceback (most recent call last) <ipython-input-16-425d66e859b8> in <module> ----> 1 print(next(iter_data))
爲何要用迭代器?
很重要的一點是,Python把迭代器內建在語言之中的,咱們在遍歷一個容器對象時並不須要去實現具體的遍歷操做。
迭代器時一個惰性序列,僅僅在迭代至當前元素時才計算該元素的值,在此以前能夠不存在,在此以後能夠隨時銷燬,也就是說,在迭代過程當中不是將全部元素一次性加載,這樣便不須要考慮內存的問題。經過定義迭代器協議,咱們能夠隨時實現一個迭代器。
何時用迭代器?
具體在什麼場景下能夠使用迭代器:
舉個最簡單的例子:
class Fib(object): def __init__(self): self._a = 0 self._b = 1 def __iter__(self): return self def __next__(self): self._a, self._b = self._b, self._a + self._b return self._a if __name__ == '__main__': for index, item in enumerate(Fib()): print(item) if index >= 9: break
什麼是生成器?
生成器,顧名思義,就是按照必定的模式生成一個序列,是一種高級的迭代器,Python中有一個專門的關鍵字(yield)來實現生成器。
若是一個函數,使用了yield語句,那麼它就是一個生成器函數,當調用生成器函數函數時,它返回一個迭代器,不過這個迭代器時一個生成器對象。
舉個例子:
from itertools import islice def fib(): a, b = 1, 1 while True: yield a a, b = b, a + b if __name__ == '__main__': fib_data = fib() print(list(islice(fib_data, 0, 10)))
能夠看到,使用生成器後,代碼簡潔了不少!在上述代碼中添加:
print(type(fib_data)) print(dir(fib_data))
能夠看到函數返回的是一個generator對象,且對象實現了迭代器協議。
可是,使用生成器必需要注意的一點是:生成器只能遍歷一次。
何時用生成器呢?
生成器能夠使用更少的中間變量來寫流式代碼, 相比於其它容器對象佔用的內存和CPU資源更少一些。當須要一個將返回一個序列或在循環中執行的函數時,就能夠使用生成器,由於當這些元素被傳遞到另外一個函數中進行後續處理時,一次返回一個元素能夠有效的提高總體性能,最重要的是,比迭代器簡潔!
除此之外,生成器還有兩個很棒的用處:
什麼是生成器表達式?
列表推導式,你們應該都用到,可是因爲內存的限制,列表的容量是有限的,若是要建立一個有幾百萬個元素的列表,會佔用不少的儲存空間,當咱們只須要訪問幾個元素時,其它元素佔用的空間就白白浪費了。
這種時候你能夠用生成器表達式啊,生成式表達式是一種實現生成器的便捷方式,將列表推導式的中括號替換爲圓括號,生成器表達式是一種邊循環邊計算,使得列表的元素能夠在循環過程當中一個個的推算出來,不須要建立完整的列表,從而節省了大量的空間。
In [1]: a = (item for item in range(10)) In [2]: type(a) Out[2]: generator In [3]: next(a) Out[3]: 0 In [4]: next(a) Out[4]: 1
以上。
代碼可參考:my github