Python 觀念紀錄 - Iteration, Iterable, Iterator
在此對 Iteration, Iterable, Iterator 的觀念做個紀錄。
了解 Iteration (迭代)
< Iteration >:
是個廣義名詞, 意思是每次去 "遍歷(走訪)" 1 個 object, 對裡面每個 item 做想操作的事的這個過程,ex: 像是在程式碼中操作 loop,我們可以說這個是一種 Iteration。
< Iterable, Iterator兩個名詞必須分清楚 >:
- Iterable
- Iterator
Iterable (可迭代物件):
在 Python 中 Iterable 和 Iterator 有特別意義,先解釋 Iterable。
Python對它的定義:
是一個物件,這個物件裡要有 __iter__()
方法或是 __getitem__()
方法(要能支持從 0 開始的index),這兩個其中一個條件達成,它就是定義上的 Iterable。
為什麼要有這兩個方法,因為 Python 說要成為 Iterable 就必須支持 Iteration Protocol 或是 Sequence Protocol。
__iter__()
對應 Iteration Protocol。
__getitem__()
對應 Sequence Protocol。
特徵:
- 是一個物件。
- 可以 for loop 遍歷的 objects 它都是定義上 Iterable 的範圍。
基本上:
- 當今天迭代的 Iterable 是 String, 那每次就是得到單個字符。
- 當今天迭代的 Iterable 是 文件,那每次得到的就是每一行的字符串。
- 當今天迭代的 Iterable 是 Dictionary, 那每次得到的就是該次的 Key。
dic, list, tuple, range, string, set, numpy array, dataframe, bytes 都是 Iterable
那 __iter__()
和 __getitem__()
是什麼?
它們是 Python Magic Methods
__iter__()
: Docs
__getitem__()
: Docs
當 __iter__()
被呼叫時,會 return 一個 Iterator(新的 Iterator)
當 __getitem__()
在 class 裡被定義, 使用object[index]形式, 就會呼叫該方法:
class Hi():
def __init__(self, name):
self.name = name
def __getitem__(self, key):
return "__getitem__ is called"
hi = Hi("Phil")
print(hi[2])
# 輸出:
# __getitem__ is called
那什麼是 Iterator,接著講:
Iterator (迭代器):
- Iterator: Docs
Python 對它的定義:
它是一個物件,這個物件裡要有 __iter__()
方法和 __next__()
方法,兩個方法都必須要有,那它就是定義上的 Iterator。
特徵:
- 是一個物件。
- Iterable 其實可以 for loop 是因為 Python 私底下幫忙轉換為 Iterator 然後做處理。
- Iterator 才可以調用
next()
, Iterable 不行。
而, dic, list, tuple, range, string, set, numpy array, dataframe, bytes 都不是 Iterator
從下面程式碼可以發現他們裡面都沒有
__next__()
,所以不會是 Iterator
|
|
那如何成為 Iterator: iter()
, next()
根據 Iterator 成立的條件,我們如果 call Builtin Function
iter()
裡面帶 Iterable,就可以 return Iterator.
所以
iter()
會 return Iterator- Iterator 裡有
next()
所以可以使用next()
next()
的作用:
Iterator 可以被 next()
調用,並不斷的返回下一個資料,直到沒有資料的時候,會拋出 StopIteration error, 而這是什麼意思呢?等等再底下有個例子可以解釋。
在 Python Docs Glossary 裡面,寫道,Iterator is an object representing a stream of data,可以看到 Iterator 是代表一個資料流的概念,也就是說程式它本身其實不知道整個長度,像是 dict
, list
, str
是一開始就知道長度的,但 Iterator 的概念是流
, 每當我需要的時候,我才通過 next()
拿到下一個我要的產出(資料), 以我的觀點 Iterator 有點像是一個 被動、懶惰的代表, 我喊它,它才動, 我不喊它, 它永遠都不動,所以他的計算是被動的,只有在需要 return 下一個資料的時候它才會行動(計算)。
ex: 範例
ss_ = "abc"
iter_ss_ = iter(ss_)
print(hasattr(iter_ss_, '__iter__')) # True
print(hasattr(iter_ss_, '__getitem__')) # False
print(hasattr(iter_ss_, '__next__')) # True
print(next(iter_ss_))
print(next(iter_ss_))
print(next(iter_ss_))
# 輸出:
"""
True
False
True
a
b
c
"""
底下這裡就會報錯因為不是 Iterator
ss_ = "abc"
next(ss_)
# 輸出
"""
True
False
True
Traceback (most recent call last):
TypeError: 'str' object is not an iterator
"""
< 關於 iter()
, __iter__()
, next()
, __next__()
> :
最一開始我也搞的滿模糊的,但要理解可以先拆分:
Built-in Function: iter()
next()
iter()
, next()
: Python Built-In Function Docs
所謂 Built-In Function, 你只要 call 它,Python 就會照著他原本寫好的劇本去跑。
Magic Methods: __iter__()
__next__()
__iter__()
, __next__()
: Python Magic Methods(Dunders)
所謂 Magic Methods, 就是由 class 裡所提供的一些 methods, Python 將這些 methods 特徵弄成前後各兩個底線,而這些 methods 之所以很 magic(自認為),是因為其實我們做的很多事情,都是他們幫我們代勞的。
代勞是什麼意思呢?
看一下這個例子:
|
|
|
|
那意思是我們可以自己客製化 object 修改這個 object 的 +
的功能
|
|
所以,結論是:
|
|
在 Python 中有數不清像 +
這樣由 __add__()
代勞的事情,
詳細可以看 Docs 3. Data model 這頁
那同理,回到主題:
其實 iter()
next()
是會去找當下的 type 所能使用的 __iter__()
, __next__()
代勞,白話一點就是 call iter()
next()
的下一瞬間,Python 就是會設法 call __iter__()
, __next__()
。
一個小結論:
|
|
用 iter()
再舉個例子:
|
|
這裡沒問題,兩個就是做一樣的事,但如果底下我們自定義呢?
這裡自訂義 class Foo:
|
|
如果改成
iter()
這時底下這裡就會出錯,因為iter()
的劇本就是堅持要拿到Iterator
, 你給他str
就肯定噴錯
|
|
小結論:
- 所有可 for loop 的 object 都是 Iterable。
- 所有可作用於
next()
的 object 都是 Iterator。 list
,str
,dict
這些是 Iterable 不是 Iterator, 但可以使用iter()
return 一個 Iterator。- 可以自己做 class 做出 Iterator, 本質上 Iterator 可以 for loop 是由
__iter__()
return self, 也就是 return object 自己去實現的,如此才能不斷繼續調用自己的__next__()
如果 class 裡只實現__next__()
,那是不能使用 for loop 的,只能調用 next() 一個一個呼叫。 - 那
next()
return 什麼? 你讓它 return 什麼它就 return 什麼。
|
|
關於前面提到 for loop 的概念:
- for loop 的本質上就是通過不斷調用
next()
實現的。
ex:
|
|
實際上相當於是:
|
|
|
|
所以 for 語句是如何 loop 的:
-
- 先判斷是否為可迭代物件(Iterable),不是的話直接報錯: TypeError: 'xxxx' object is not iterable,是的話調用
__iter__()
,並 return 一個迭代器(Iterator)
- 先判斷是否為可迭代物件(Iterable),不是的話直接報錯: TypeError: 'xxxx' object is not iterable,是的話調用
-
- 不斷的調用迭代器(Iterato)的
__next__()
方法,每次都按照順序返回迭代器(Iterator)中的一個值
- 不斷的調用迭代器(Iterato)的
-
- 迭代到最後,已經沒有元素了,這時就會拋出 StopIteration,但是這個異常就像上面的例子一樣會做處理,Python 自己會處理掉,不會給 developer 看。
收尾:
- Iteration(迭代): 可以指一個概念,輪廓,操作過程,是一個廣義名詞,在程式中的例子有 for loop
- Iterable(可迭代物件): 一個物件,根據 Python 的定義,得出結論:
- 如果可以 for loop 它,就是 Iterable 的範疇
- 但 Iterable 可以 for loop 是因為 Python 幫我們把它轉變成 Iterator
- Iterator(迭代器): 一個物件,根據 Python 的定義,得出結論:
- Python for loop 的原理就是需要 Iterator,然後不斷調用 Iterator 的
__next__()
- 產生 Iterator 的方式:
- iter()
- 自己客製化 class
- 產生 generator iterator (或是叫做 generator object / generator)(是一種 Iterator,下篇會敘述)
- Python for loop 的原理就是需要 Iterator,然後不斷調用 Iterator 的
The End:
< Self Checklist >:
- Explain what is
Iteration
. - Explain what is
Iterable
. - Explain what is
Iterator
. - Can you tell what is the difference between
Iterable
andIterator
. - Explain what is
Built-in Function
. - Explain what is
Magic Methods
. - Explain how Python
for loop
works. - Use
hasattr()
,next()
,iter()
< Reference >:
- Python
__iter__()
- Python
__getitem__()
- Python Iterator
- Python Glossary
- Python Built-In Function
- Python Docs 3. Data model
- 廖雪峰 Python 3.x
- 迭代那件小事:深入了解 Iteration / Iterable / Iterator / iter / getitem / next / yield