迭代器和生成器,是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__()
方法,實現迭代器;
我的公眾號:猿人學 Python 上會分享更多心得體會,敬請關注。
***版權申明:若沒有特殊說明,文章皆是猿人學 yuanrenxue.com 原創,沒有猿人學授權,請勿以任何形式轉載。***