1. 程式人生 > >Python學習——02-Python基礎——【5-叠代器協議和生成器】

Python學習——02-Python基礎——【5-叠代器協議和生成器】

expr filter exp 就是 ati another 檢索 traceback AD

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.next
11 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從數據中分離出來,分別定義一個iterableiterator如下:

 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-叠代器協議和生成器】