迭代器是一種支持next()操作的對象。它包含一組元素,當執行next()操作時,返回其中一個元素;當所有元素都被返回后,生成一個StopIteration異常。
1
2
3
4
5
6
7
8
9
10
11
12
|
>>>a = [ 1 , 2 , 3 ] >>>ia = iter (a) >>> next (ia) 1 >>> next (ia) 2 >>> next (ia) 3 >>> next (ia) Traceback (most recent call last): File "<stdin>" , line 1 , in <module> StopIteration |
ite()可以接受多種Python對象為參數,比如list,tuple, dict, set等,并將其轉化為迭代器。迭代器可以用于for語句或in語句中。很多常用操作也是支持迭代器的,比如sum(), max()等。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
>>> b=[4,5,6] >>> ib=iter(b) >>> for x in ib: ... print(x) ... 4 5 6 >>> ic=iter(b) >>> sum(ic) 15 >>> id=iter(b) >>> max(ic) 6 |
毋庸置疑,迭代器有很多好處:
1.“流式”數據處理方式減少內存消耗:
比如處理文件,一下猛地把全部數據全部取出來放到內存里面進行處理會導致程序消耗大量內存,有時甚至沒法做到,一般我們會一部分一部分的對文件內容進行處理:
1
2
|
for text_line in open ( "xx.txt" ): print text_line |
2.或者對xml文件進行處理的時候:
1
2
3
4
5
6
|
tree = etree.iterparse(xml, [ 'start' , 'end' ]) for event, elem in tree: if event = = "end" result = etree.tostring(elem) elem.clear() print result |
內置函數open返回的file對象和etree.iterparse序列化的xml tree都是可迭代對象,能夠讓我們漸進式地對文件的內容進行處理。
3.支持方便用for語句對數據進行消費:
python內置的一些常見的像類型像數組、列表甚至字符串等都是可迭代類型,這樣我們就能方便for語句這個語法糖方便對數據進行消費,不需要自己記錄索引位置,人肉循環:
1
2
|
for i in [ 1 , 2 , 3 , 4 ] print i, |
簡單了解了一下迭代器的好處后,我們正正經經的聊聊python的迭代器模式。
在這里我們引入兩個比較繞口的名詞:可迭代對象和迭代器對象,個人覺得從這兩個概念下手會對迭代器有比較好的理解。在放例子前先對這兩個概念給一個不入流的解釋:
可迭代對象:對象里面包含__iter()__方法的實現,對象的iter函數經調用之后會返回一個迭代器,里面包含具體數據獲取的實現。
迭代器:包含有next方法的實現,在正確范圍內返回期待的數據以及超出范圍后能夠拋出StopIteration的錯誤停止迭代。
放個例子邊看邊說:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
class iterable_range: def __init__( self , n): self .n = n def __iter__( self ): return my_range_iterator( self .n) class my_range_iterator: def __init__( self , n): self .i = 0 self .n = n def next ( self ): if self .i < self .n: i = self .i self .i + = 1 print 'iterator get number:' , i return i else : raise StopIteration() |
例子中的iterable_range是一個可迭代對象,所以我們也能夠對它用for語句來進行迭代:
1
2
3
|
temp = my_range( 10 ) for item in temp: print item, |
輸出:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
my iterator get number: 0 0 my iterator get number: 1 1 my iterator get number: 2 2 my iterator get number: 3 3 my iterator get number: 4 4 my iterator get number: 5 5 my iterator get number: 6 6 my iterator get number: 7 7 my iterator get number: 8 8 my iterator get number: 9 9 |
大家可以仔細地看一下輸出的日志:
- 數據確實是“流式”處理的
- iterator是真正在背后做事的人
- for語句能夠非常方便的迭代對象的數據。
可迭代對象其實更像是整個迭代器模式模式的上層,像一種約束一種契約一種規范,它能夠保證自己能夠返回一個在實際工作中干活的迭代器對象。for、sum等接受一個可迭代對象的方法都是遵循這樣的規范:調用對象的__iter__函數,返回迭代器,對迭代器對象返回的每個值進行處理抑或需要一些匯總的操作。拿for舉個例子:
1
2
3
4
5
6
7
8
9
10
|
iterator_object = iterable_object.__iter__() while True : try : value = iterator_object. next () except StopIteration: # StopIteration exception is raised after last element break # loop code print value |
for這個語法糖背后的邏輯差不多就是上面例子中代碼所示的那樣:首先獲取可迭代對象返回的迭代器對象,然后調用迭代器對象的next方法獲取每個值,在獲取值的過程中隨時檢測邊界-也就是檢查是否拋出了StopIteration這樣的錯誤,如果迭代器對象拋出錯誤則迭代停止(note:從這個例子可以看出,對于那些接受可迭代對象的方法,如果我們傳一個單純的迭代器對象其實也是無法工作的,可能會報出類似于TypeError: iteration over non-sequence的錯誤)。
當然了,一般在應用過程中我們不會將他們特意的分開,我們能夠稍微對迭代器對象進行修改一下,添加__iter__方法的實現,這樣對象本身就既是可迭代對象也是一個迭代器對象了:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
class my_range_iterator: def __init__( self , n): self .i = 0 self .n = n def __iter__( self ): return self def next ( self ): if self .i < self .n: i = self .i self .i + = 1 print 'my iterator get number:' , i return i else : raise StopIteration() for item in my_range_iterator( 10 ): print item |
輸出:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
my iterator get number: 0 0 my iterator get number: 1 1 my iterator get number: 2 2 my iterator get number: 3 3 my iterator get number: 4 4 my iterator get number: 5 5 my iterator get number: 6 6 my iterator get number: 7 7 my iterator get number: 8 8 my iterator get number: 9 9 |