Python中的iterable、iterator和generator(一)

對Python初學者來說,要搞清楚其中的iterable、iterator和generator的分別可不容易。看表面意思,iterable應該是一種可以迭代的東西,iterator是一個用來迭代的東西,generator是一個能產生東西的東西。可是,iterable不是iterator,但卻可以從它身上得到iterator,而iterator也是iterable的,可以從身上得到自己這個iterator,最後generator是個iterator,所以也是iterable的...在說甚麼呢?不急,我們一步步分析。

首先,iterable顯然是一種可以迭代的東西,既然如此,那麼str、list、tuple、set、dict等預設的Python objects都是iterable,都可以將它們用for語句做迭代,例如:

>>> names = ['Mary', 'Jack', 'Emily', 'Lauren', 'Olivia']                                                
>>> for name in names:
...     print(name)    
... 
Mary
Jack
Emily
Lauren
Olivia

那iterable可以產生iterator又是甚麼意思呢?用dir函數看看list這個iterable有甚麼方法可以調用:

>>> dir(names)
['__add__', '__class__', '__contains__', '__delattr__', '__delitem__',
 '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__',
 '__getitem__', '__gt__', '__hash__', '__iadd__', '__imul__', '__init__',
 '__init_subclass__', '__iter__', '__le__', '__len__', '__lt__', '__mul__',
 '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__',
 '__reversed__', '__rmul__', '__setattr__', '__setitem__', '__sizeof__',
 '__str__', '__subclasshook__', 'append', 'clear', 'copy', 'count', 'extend', 
'index', 'insert', 'pop', 'remove', 'reverse', 'sort']

返回的結果很長,但我們要注意,names這個list中有一個叫__iter__的方法,就是iterable中的這個方法可以產生一個iterator。Python已內建了iter函數讓我們調用object中的__iter__方法,不用寫為names.__iter__()的樣子:

>>> iter(names)
<list_iterator object at 0x03085190>

產生這個iterator用來幹嘛呢?再用dir函數看看它有甚麼方法可調用:

>>> names_iter = iter(names)
>>> dir(names_iter)
['__class__', '__delattr__', '__dir__', '__doc__', '__eq__', '__format__',
 '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__',
 '__init_subclass__', '__iter__', '__le__', '__length_hint__', '__lt__',
 '__ne__', '__new__', '__next__', '__reduce__', '__reduce_ex__', '__repr__',
 '__setattr__', '__setstate__', '__sizeof__', '__str__', '__subclasshook__']

注意了,iterator和iterable最大的分別是iterator有一個叫__next__的方法,這個方法可以返回序列中的下一個內容。由於Python也內建了next函數調用object中的__next__方法,所以不用寫為names_iter.__next__()

>>> next(names_iter)
'Mary'
>>> next(names_iter)
'Jack'
>>> next(names_iter)
'Emily'

可見,每調用一次__next__方法,序列中的內容就會被讀出一個,直到讀完所有內容,就會引發StopIteration異常:

>>> next(names_iter)
'Lauren'
>>> next(names_iter)
'Olivia'
>>> next(names_iter)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
StopIteration

另一方面,iterable是沒有__next__方法的,你不能用next來從iterable中將序列內容讀出:

>>> next(names)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: 'list' object is not an iterator

不過,iterator中卻有__iter__方法,這不是iterable中用於產生iterator的方法嗎?究竟是怎麼一回事呢?用names_iter中的__iter__方法嘗試產生另一個iterator,但會發現這個iterator就是自己本身:

>>> names_iter2 = iter(names_iter)
>>> names_iter
<list_iterator object at 0x03085280>
>>> names_iter2 
<list_iterator object at 0x03085280>
>>> names_iter is names_iter2 
True

可見names_iternames_iter2是相同的object,既然它們相同,那麼對names_iter2使用next函數,也一樣沒有內容可以讀出了,也會出現StopIteration異常:

>>> next(names_iter2)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
StopIteration

小結一下:

  • iterable中有__iter__方法,調用它會產生一個對應的iterator。
  • iterator中有__next__方法,調用它會返回對應iterable序列中的內容。
  • iterator中也有__iter__方法,調用它會返回自己,由於iterator有iterable中的__iter__方法,所以說iterator也是iterable的。

我們在下一次再介紹為何要有iterable和iterator,為何不二合為一。

4.9 10 votes
Article Rating
Subscribe
Notify of
guest
0 Comments
Inline Feedbacks
View all comments