0%

Iterators的概念與如何使用

python中有些資料結構是可以透過for來loop裡面所有的item的,像是list, tuple, dict, str還有file都可以。在使用上其實for迴圈會呼叫物件中的iter(),此函式會回傳一個Iterator(迭代器),Iterator會透過next函式來一次一次的取得下一個item,並在沒有下一個item的時候拋出StopIteration來告訴for迴圈迭代結束。

像這種有按照Iteration Protocol定義iternext的物件又被稱為Iterable

1
2
3
4
5
>>> for item in [1, 2, 3, 4]
>>> for item in (1, 2, 3, 4)
>>> for item in {'one': 1, 'two': 2}
>>> for item in 'python'
>>> for line in open('file')

如果希望自己定義的物件是Iterable,只要透過在class內實作iternext(讓python的build-in函式iter()和next()可以呼叫)就可以達成。

假設我們建立了一個物件可以設定一個字串,並在每次iteration的時候透過ascii回傳每個字元的下一個字元

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class Transform:    
def init(self, data):
self.data = data
self.i = 0
self.n = len(data)

def iter(self):
return self

def next(self):
if self.i == self.n:
raise StopIteration
item = chr(ord(self.data[self.i]) + 1)
self.i = self.i + 1
return item

宣告一個Transform物件,並用for來讀取每一個值

1
2
3
4
5
6
7
8
9
>>> t = Transform('python')
>>> for item in t: # for迴圈自動叫iter(),並用next()來作iteration
>>> print(item)
q
z
u
i
p
o

宣告一個Transform物件,並呼叫iter並用next來讀取每一個值

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
>>> t = Transform('python')
>>> t2 = iter(t)
>>> next(t2)
q
>>> next(t2)
z
>>> next(t2)
u
>>> next(t2)
i
>>> next(t2)
p
>>> next(t2)
o
>>> next(t2) # 當沒有下一個item之前,會產生StopIteration
Traceback (most recent call last):
File <stdin>, line 1, in
next(t2)
StopIteration

除了透過定義iternext讓自己的物件為iterable,也可以透過在class中定義getitem來達成。兩者只要有定義其中一種,都可以讓for loop來依序讀取物件中的item

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class Transform:    
def init(self, data):
self.data = data
self.i = 0
self.n = len(data)

def getitem(self, i):
return chr(ord(self.data[i]) + 1)

>>> t = Transform('python')
>>> for item in t: # 透過定義__getitem__也可以讓物件為iterable,並可以使用在for loop
>>> print(item)
q
z
u
i
p
o

參考資料:
Python docs - Iterators
Python docs - Iterator types
Python docs - Iterable