Python 中的 Iterator 是什麼
在 Python 中,iterator (迭代器)是一種特殊的物件,它可以逐步遍歷序列中的每一個元素。與 list 不同,iterator 不會在記憶體中保存整個序列,而是在需要時逐步計算出下一個元素的值,從而節省記憶體空間。這篇文章會說明什麼是 iterator,iterator 的好處以及它與 generator 或 yield 關鍵字的關係。
什麼是 Iterator
我們在 Python 中經常使用 for
迴圈來處理序列、進行迭代操作,Python 中的 iterator 物件就是用來支援這種迭代操作的,它可以讓我們逐一取出序列中的元素。我們可以使用 iter()
函數來建立一個 iterator 物件,並使用 next()
方法來取得下一個元素。如以下範例:
在這個範例中,我們先建立一個包含五個元素的 list,接著使用 iter()
函式建立一個 iterator 物件 my_iterator
,然後使用 next()
方法依序取得它的元素。範例程式碼中的 next()
方法依序取得了 my_iterator
的前三個元素(也就是 1、2、3)。
Iterator 的好處
使用 iterator 的優點在於節省記憶體。當你使用 iterator 處理大量資料時,它只會在需要時才生成資料,而不是一次生成整個序列。舉例來說,假設你需要建立一個包含一百萬個元素的 list,這一百萬個元素會一次被產生出來,並佔用記憶體空間。但是,如果你使用 iterator,你可以透過 generator expression 依序生成這些元素,而不需要在記憶體中一次儲存整個 list。如以下範例:
你可以看到這兩個變數佔用的記憶體空間差距有多大(8 MB 與 112 bytes),當資料量很龐大時,使用 iterator 的好處會更明顯。
「只在需要時產生元素」的特性,也可以理解為 Lazy Loading (或 Lazy Evaluation). 因為一次只處理序列中的一個元素,iterator 的高效率在需要大量計算才能產生序列中的元素時特別明顯。如以下範例:
get_with_list()
會把整個序列處理完且產生之後才回傳結果,所以 for n in get_with_list(numbers)
這個迴圈大約需要 1+1+5+1+10 秒;而 get_with_generator()
會依序處理 elements
裡面的元素,且一次回傳一個結果,所以 for n in get_with_generator(numbers)
這個迴圈只需要 1+1+5 秒(numbers
內最後兩個數字被處理到之前,迴圈已結束)。
for 迴圈也可以遍歷 list,那 list 是 iterator 嗎?你搞得我好亂啊
用 for 迴圈遍歷一個 list 與一個 iterator 的結果是完全相同的:
但是,list 不是 iterator, 只是個 iterable (可迭代) 類別。在 Python 中,任何可迭代物件都可以被 for 迴圈使用,而不僅僅限於 iterator.
當我們使用 for 迴圈迭代 iterable 物件時,Python 會在幕後自動呼叫物件的 iter()
方法取得一個 iterator 物件,再利用該物件依次取得序列中的每個數值。所以在以上例子中,Python 會自動呼叫my_list
的 iter()
方法,取得一個 iterator 物件,然後再呼叫該物件的 next()
方法,逐個取得序列中的每個數值。遍歷 my_list
與 my_iterator
的輸出結果相同,但這兩個變數佔用的記憶體空間不同,一開始的例子已經說明過了。
Generator 跟 yield 是什麼?它們跟 iterator 有什麼關係?
Generator 是 iterator 的一種實現方式。generator 是一種使用函式來實現的 iterator,它可以動態生成序列,並且每次只生成一個值。generator 的運作方式類似於函式,它可以接受一些參數,然後根據這些參數生成一個序列。
Generator 的語法非常簡單,只需要在函式中使用 yield 關鍵字(而不是 return)回傳結果即可,例如以下範例:
用 yield
取代 return
的差異在於:它讓 countdown()
函數可以記住上一次迭代時的狀態。當 countdown()
第一次被呼叫時,它會停在 yield n
這一行,並回傳 10;第二次被呼叫時,它會從上一次停止的地方繼續執行(也就是 yield n
的下一行,n -= 1
),直到再次遇到 yield
,才暫停並回傳 9……以此類推。換句話說,countdown()
函數每次只生成一個值,而不是一次性在記憶體中產生整個序列。
如何自定義一個 Iterator 物件
只需要定義__iter__()
和__next__()
這兩個必要方法即可。其中,__iter__()
方法返回迭代器物件本身,而__next__()
方法則返回下一個值。當序列中沒有值時,__next__()
方法應該拋出StopIteration 異常。
以下是一個簡單的迭代器例子,它能夠生成指定範圍內的奇數: