Python 迭代器

veelion發表於2019-03-17

迭代器和生成器,是Python語言的一大特定。這兩個概念我們可能還不理解,但是我們前面的學習中已經有所接觸,比如用for...in遍歷一個列表、字典、元組,就是使用了迭代器。

迭代器

可迭代物件(iterable)

這個概念我們前面已經學過,這裡提到是為了更好理解迭代器。從字面上看,可迭代物件和迭代器有很大關係,迭代器作用在可迭代物件上面,逐一返回可迭代物件的元素。也就是說,可以透過for迴圈遍歷的物件就可以稱為可迭代物件。Python內建的資料型別中,是可迭代物件的有:str, list, tuple, dict, set。這些資料結構都是用來存放其它物件的,也稱為“容器”。

迭代器(iterator)

for迴圈遍歷列表(我們以列表為例,元組和字典等類似)的背後就是迭代器起的作用,Python有兩個內建函式iter()next()可以把這些可迭代物件顯示轉換為迭代器並進行遍歷。先看看下面的程式碼:

In [1]: ll = [1,2,3]

In [2]: for n in ll: 
   ...:     print(n) 
   ...:
1
2
3

In [3]: itr = iter(ll)

In [4]: itr?
Type:        list_iterator
String form: <list_iterator object at 0x7fc0483c9828>
Docstring:   <no docstring>

In [5]: next(itr)
Out[5]: 1

In [6]: next(itr)
Out[6]: 2

In [7]: next(itr)
Out[7]: 3

In [8]: next(itr)
----------------------------------------------------
StopIteration         Traceback (most recent call last)
<ipython-input-8-6693cc261707> in <module>
----> 1 next(itr)

StopIteration: 

透過iter()內建函式,我們就可以得到list物件的迭代器(這實際上是呼叫的ll.__iter__()),得到的是一個list_iterator物件;然後我們就可以用內建函式next()遍歷這個迭代器(實際上是呼叫的ll.__next__())。每次呼叫next()都能得到ll的一個元素,其是按順序依次返回ll的元素,也就是說,迭代器機制了當前元素的位置。當元素遍歷完畢時,就會引發StopIteration的異常。從此,我們最初得到的迭代器itr就不可以再迭代,因為它已經迭代到末尾。

透過上面的例子,我們可以看出一些迭代器的端倪。其實,迭代器就是定義了__iter__()方法來返回一個帶有__next__()方法的物件的物件。按照這個定義,我們不用內建函式而是用__iter__()__next__()來迭代一下列表物件:

In [9]: ll
Out[9]: [1, 2, 3]

In [10]: itr = ll.__iter__()

In [11]: itr
Out[11]: <list_iterator at 0x7fc048dcdcc0>

In [12]: itr.__next__()
Out[12]: 1

In [13]: itr.__next__()
Out[13]: 2

In [14]: itr.__next__()
Out[14]: 3

In [15]: itr.__next__()
----------------------------------------------
StopIteration       Traceback (most recent call last)
<ipython-input-15-fc6e27a9fb4c> in <module>
----> 1 itr.__next__()

StopIteration: 

可以看出,這實現了和內建函式iter()next()同樣的效果。實際上,內建函式iter()就是呼叫了迭代器的__iter__()方法,而next()就是呼叫了迭代器的__next__()方法。

明白了迭代器的內部機制(稱為“迭代器協議”),我們就可以在自定義類中新增迭代器行為了:

In [21]: class LessThan: 
    ...:     '''遍歷比給定整數小的正整數的迭代器''' 
    ...:     def __init__(self, n):  
    ...:         self.n = n  
    ...:   
    ...:     def __iter__(self): 
    ...:         return self 
    ...:   
    ...:     def __next__(self):
    ...:           
    ...:         if self.n == 0: 
    ...:             raise StopIteration 
    ...:         self.n -= 1 
    ...:         return self.n 

In [23]: lt = LessThan(5)

In [24]: lt
Out[24]: <__main__.LessThan at 0x7fc048d3acc0>

In [25]: iter(lt)
Out[25]: <__main__.LessThan at 0x7fc048d3acc0>

In [26]: for n in lt: 
    ...:     print(n, end=' ') 
    ...:
4 3 2 1 0 

lt是我們自定義類LessThan的一個物件,透過內建函式iter()可以得到它的迭代器,它們兩個完全一樣,都是<__main__.LessThan at 0x7fc048d3acc0>,因為內建函式和iter()其實呼叫的就是lt.__iter__()方法,而這個方法返回的就是它本身(帶有__next__()方法)。

總結

(1)可迭代物件:
可以透過for迴圈遍歷的物件都是可迭代物件。本質上,定義了__iter__()方法或__getitem__()方法的物件就是可迭代物件。

(2)迭代器:
定義了__next__()方法的物件就是一個迭代器。迭代器迭代到末尾就會引起Stopiteration異常。

(2)內建函式iter()next()
iter()作用於可迭代物件得到迭代器;next()對迭代器進行遍歷。

(3)自定義類
自定義類中定義__iter__()方法和__next__()方法,實現迭代器;

猿人學banner宣傳圖

我的公眾號:猿人學 Python 上會分享更多心得體會,敬請關注。

***版權申明:若沒有特殊說明,文章皆是猿人學 yuanrenxue.com 原創,沒有猿人學授權,請勿以任何形式轉載。***

相關文章