Python學習——02-Python基礎——【5-叠代器協議和生成器】
1.叠代器(iterator)
要說生成器,必須首先說叠代器
1.區分iterable,iterator與itertion
講到叠代器,就需要區別幾個概念:iterable
,iterator
,itertion
, 看著都差不多,其實不然。下面區分一下。
-
itertion
: 就是叠代
,一個接一個(one after another),是一個通用的概念,比如一個循環遍歷某個數組。 -
iterable
: 這個是可叠代對象
,屬於python的名詞,範圍也很廣,可重復叠代,滿足如下其中之一的都是iterable
:-
可以
for
循環:for i in iterable
-
可以按
index
索引的對象,也就是定義了__getitem__
list,str
; -
定義了
__iter__
方法。可以隨意返回。 -
可以調用
iter(obj)
的對象,並且返回一個iterator
-
-
iterator
:叠代器對象
,也屬於python的名詞,只能叠代一次。需要滿足如下的叠代器協議
-
定義了
__iter__
方法,但是必須返回自身 -
定義了
next
方法,在python3.x是__next__
。用來返回下一個值,並且當沒有數據了,拋出StopIteration
-
可以保持當前的狀態
-
1 In [3]: s = ‘hi‘ 2 3 In [4]: s.__getitem__ 4 Out[4]: <method-wrapper ‘__getitem__‘ of str object at 0x7f9457eed580> 5 6 In [5]: s.next # 沒有next方法 7 --------------------------------------------------------------------------- 8 AttributeError Traceback (most recent call last) 9 <ipython-input-5-136d3c11be25> in <module>() 10 ----> 1 s.next11 12 AttributeError: ‘str‘ object has no attribute ‘next‘ 13 14 In [6]: l = [1,2] # 同理 15 16 In [7]: l.__iter__ 17 Out[7]: <method-wrapper ‘__iter__‘ of list object at 0x7f945328c320> 18 19 In [8]: l.next 20 --------------------------------------------------------------------------- 21 AttributeError Traceback (most recent call last) 22 <ipython-input-8-c6f8fb94c4cd> in <module>() 23 ----> 1 l.next 24 25 AttributeError: ‘list‘ object has no attribute ‘next‘ 26 In [9]: iter(s) is s #iter() 沒有返回本身 27 Out[9]: False 28 In [10]: iter(l) is l #同理 29 Out[10]: False
但是對於iterator
則不一樣如下, 另外iterable
可以支持多次叠代,而iterator
在多次next
之後,再次調用就會拋異常,只可以叠代一次。
1 In [13]: si = iter(s) 2 3 In [14]: si 4 Out[14]: <iterator at 0x7f9453279dd0> 5 6 In [15]: si.__iter__ # 有__iter__ 7 Out[15]: <method-wrapper ‘__iter__‘ of iterator object at 0x7f9453279dd0> 8 9 In [16]: si.next #擁有next 10 Out[16]: <method-wrapper ‘next‘ of iterator object at 0x7f9453279dd0> 11 12 In [20]: si.__iter__() is si #__iter__返回自己 13 Out[20]: True
這樣,由這幾個例子可以解釋清楚這幾個概念的區別。
2.自定義iterator 與數據分離
說到這裏,叠代器對象基本出來了。下面大致說一下,如何讓自定義的類的對象成為叠代器對象,其實就是定義__iter__
和next
方法:
1 In [1]: %paste 2 class DataIter(object): 3 4 def __init__(self, *args): 5 self.data = list(args) 6 self.ind = 0 7 8 def __iter__(self): #返回自身 9 return self 10 11 def next(self): # 返回數據 12 if self.ind == len(self.data): 13 raise StopIteration 14 else: 15 data = self.data[self.ind] 16 self.ind += 1 17 return data 18 ## -- End pasted text -- 19 20 In [9]: d = DataIter(1,2) 21 22 In [10]: for x in d: # 開始叠代 23 ....: print x 24 ....: 25 1 26 2 27 28 In [13]: d.next() # 只能叠代一次,再次使用則會拋異常 29 --------------------------------------------------------------------------- 30 StopIteration Traceback (most recent call last) 31 ----> 1 d.next() 32 <ipython-input-1-c44abc1904d8> in next(self) 33 10 def next(self): 34 11 if self.ind == len(self.data): 35 ---> 12 raise StopIteration 36 13 else: 37 14 data = self.data[self.ind]
從next
函數中只能向前取數據,一次取一個可以看出來,不過不能重復取數據,那這個可不可以解決呢?
我們知道iterator
只能叠代一次,但是iterable
對象則沒有這個限制,因此我們可以把iterator
從數據中分離出來,分別定義一個iterable
與iterator
如下:
1 class DataIterator(object): # iterator: 叠代器 2 3 def __init__(self, data): 4 self.data = data.data 5 self.ind = 0 6 7 def __iter__(self): 8 return self 9 10 def next(self): 11 if self.ind == len(self.data): 12 raise StopIteration 13 else: 14 data = self.data[self.ind] 15 self.ind += 1 16 return data 17 18 if __name__ == ‘__main__‘: 19 d = Data(1, 2, 3) 20 for x in d: 21 print x, 22 for x in d: 23 print x,
輸出:
1,2,3 1,2,3
可以看出來數據可以復用,因為每次都返回一個DataIterator
,但是數據卻可以這樣使用,這種實現方式很常見,比如xrange
的實現便是這種數據與叠代分離的形式,但是很節省內存,如下:
1 In [8]: sys.getsizeof(range(1000000)) 2 Out[8]: 8000072 3 4 In [9]: sys.getsizeof(xrange(1000000)) 5 Out[9]: 40
另外有個小tips, 就是為什麽可以使用for 叠代叠代器對象,原因就是for
替我們做了next
的活,以及接收StopIteration
的處理。
叠代器大概就記錄到這裏了,下面開始一個特殊的更加優雅的叠代器: 生成器
1.生成器(generator)
首先需要明確的就是生成器也是iterator
叠代器,因為它遵循了叠代器協議.
1.兩種創建方式
1.包含yield
的函數
生成器函數跟普通函數只有一點不一樣,就是把 return
換成yield
,其中yield
是一個語法糖,內部實現了叠代器協議,同時保持狀態可以掛起。如下:
記住一點,yield
是數據的生產者,而諸如for
等是數據的消費者。
1 def gen(): 2 print ‘begin: generator‘ 3 i = 0 4 while True: 5 print ‘before return ‘, i 6 yield i 7 i += 1 8 print ‘after return ‘, i 9 10 a = gen() 11 12 In [10]: a #只是返回一個對象 13 Out[10]: <generator object gen at 0x7f40c33adfa0> 14 15 In [11]: a.next() #開始執行 16 begin: generator 17 before return 0 18 Out[11]: 0 19 20 In [12]: a.next() 21 after return 1 22 before return 1 23 Out[12]: 1
首先看到while True
不必驚慌,它只會一個一個的執行~
看結果可以看出一點東西:
-
調用
gen()
並沒有真實執行函數,而是只是返回了一個生成器對象 -
執行第一次
a.next()
時,才真正執行函數,執行到yield
一個返回值,然後就會掛起,保持當前的名字空間等狀態。然後等待下一次的調用,從yield
的下一行繼續執行。
還有一種情況也會執行生成器函數,就是當檢索生成器的元素時,如list(generator)
, 說白了就是當需要數據的時候,才會執行。
In [15]: def func(): ....: print ‘begin‘ ....: for i in range(4): ....: yield i In [16]: a = func() In [17]: list(a) #檢索數據,開始執行 begin Out[17]: [0, 1, 2, 3]
yield
還有其他高級應用,後面再慢慢學習。
2.生成器表達式
列表生成器十分方便:如下,求10以內的奇數:[i for i in range(10) if i % 2]
同樣在python 2.4
也引入了生成器表達式
,而且形式非常類似,就是把[]
換成了()
.
1 In [18]: a = ( i for i in range(4)) 2 3 In [19]: a 4 Out[19]: <generator object <genexpr> at 0x7f40c2cfe410> 5 6 In [20]: a.next() 7 Out[20]: 0
可以看出生成器表達式創建了一個生成器,而且生有個特點就是惰性計算
, 只有在被檢索時候,才會被賦值。
之前有篇文章:python 默認參數問題及一個應用,最後有一個例子:
1 def multipliers(): 2 return (lambda x : i * x for i in range(4)) #修改成生成器 3 print [m(2) for m in multipliers()]
這個就是說,只有在執行m(2)
的時候,生成器表達式裏面的for
才會開始從0循環,然後接著才是i * x
,因此不存在那篇文章中的問題.
惰性計算這個特點很有用,上述就是一個應用,2gua這樣說的:
個人理解就是就是可以利用生成器來作為數據管道使用,當被檢索的時候,每次拿出一個數據,然後向下面傳遞,傳到最後,再拿第二個數據,在下面的例子中會詳細說明。
其實本質跟叠代器差不多,不一次性把數據都那過來,需要的時候,才拿。
基礎版:http://www.runoob.com/python3/python3-iterator-generator.html
3.三元運算
三元運算是if-else 語句的快捷操作,也被稱為條件運算。
1 #樣式:[on_true] if [expression] else [on_false] 2 #例子: 3 x, y = 50, 25 4 small = x if x < y else y 5 #還可以嵌套使用,然當還可以更多層嵌套 6 a,b,c = 10,20,5 7 minValue = a if a < b and a < c else (b if b < a and b < c else c)
Python學習——02-Python基礎——【5-叠代器協議和生成器】