1. 程式人生 > >Python之叠代器

Python之叠代器

time {} 消費者模式 ati tro 計算 bre body 存儲

一、叠代器

有時候我們在定義一個列表的時候,列表中的元素是具有一定規則的,這時候可以用列表生成式來提高一下我們的逼格。具體語法如下:

1 list = [i*3 for i in range(10)] #列表生成式 等價於 list = [0, 3, 6, 9, 12, 15, 18, 21, 24, 27]
2 print(list) #[0, 3, 6, 9, 12, 15, 18, 21, 24, 27]

理解了列表生成式的語法,生成器的寫法就很容易了,只要將[]改成()即可,生成器的語法如下:

1 list_generate =  (i*3 for i in range(10)) #
列表生成器 2 print(list_generate) #執行結果<generator object <genexpr> at 0x03327C30>

列表生成式是在執行程序之前,就將數據生成並存放在內存中,而生成器是一個算法,只有程序在執行的過程中才會生成當前所需要的數據。下圖是這兩種方式在內存的存儲方式:

技術分享圖片

因此可以得出兩種的區別:1、列表生成式占用內存,取的速度快 2、生成器占用很少內存,取的速度慢

對於生成器而言,要想獲取生成器中的元素,需要每次調用其__next__()方法,如果我們的生成器有100萬條數據,那我敢肯定你一定會瘋掉的,因此我們一般用循環來取生成器中的數據,例如:

1 list_generate =  (i*3 for i in range(10)) #列表生成器
2 for i in list_generate:
3     print(i)

函數生成器

使用關鍵字yield定義生成器。

1 def getNo():
2     for i in  range(10):
3         yield i
4 f = getNo()
5 print(f.__next__())

其中f就是一個生成器,下面我們利用函數生成器來實現單線程下的並發效果(著名的生產者消費者模式)。

 1 import time
 2 def consmer(name):
3 print("%s準備吃包子了。" % name) 4 while True: 5 baozi = yield 6 print("%s號包子來了,被%s吃了" % (baozi, name)) 7 def producter(name): 8 eat_p = consmer("吃貨") 9 eat_p1 = consmer("餓貨") 10 eat_p.__next__() 11 eat_p1.__next__() 12 for i in range(1,100,2): 13 print("老子開始做包子了") 14 time.sleep(2) 15 print("%s做了2個包子" % name) 16 eat_p.send(i-1) 17 eat_p1.send(i) 18 producter("高文祥")

Iterable(可叠代對象)

我們已經知道,可以直接作用於for循環的數據類型有以下幾種:一類是集合數據類型,如listtupledictsetstr等;一類是generator,包括生成器和帶yield的generator function。這些可以直接作用於for循環的對象統稱為可叠代對象:Iterable。可以使用isinstance()判斷一個對象是否是Iterable對象。

from collections import  Iterable
print(isinstance([],Iterable),isinstance({},Iterable),isinstance(set,Iterable),isinstance("abc",Iterable))#執行結果True True False True

然而,生成器不但能夠作用於for循環,還具有__next__()方法,像這種可以被next()函數調用並不斷返回下一個值的對象稱為叠代器:Iterator。可以使用isinstance()判斷一個對象是否是Iterator對象。

1 from collections import  Iterable,Iterator
2 print(isinstance([],Iterator),isinstance({},Iterator),isinstance(set,Iterator),isinstance("abc",Iterator),isinstance((i*2 for i in range(10)),Iterator))#執行結果False False False False True

然而,我們可以通過iter()方法把可叠代對象轉換為叠代器

1 from collections import  Iterable,Iterator
2 print(isinstance(iter([]),Iterator))

那為什麽像list,字典都不是叠代器呢?

這是因為Python的Iterator對象表示的是一個數據流,Iterator對象可以被next()函數調用並不斷返回下一個數據,直到沒有數據時拋出StopIteration錯誤。可以把這個數據流看做是一個有序序列,但我們卻不能提前知道序列的長度,只能不斷通過next()函數實現按需計算下一個數據,所以Iterator的計算是惰性的,只有在需要返回下一個數據時它才會計算。

Iterator甚至可以表示一個無限大的數據流,例如全體自然數。而使用list是永遠不可能存儲全體自然數的。

總結:

1、凡是可作用於for循環的對象都是Iterable類型;

2、凡是可作用於next()函數的對象都是Iterator類型,它們表示一個惰性計算的序列;

3、集合數據類型如listdictstr等是Iterable但不是Iterator,不過可以通過iter()函數獲得一個Iterator對象。

4、Python的for循環本質上就是通過不斷調用next()函數實現的,例如:

1 for x in [1, 2, 3, 4, 5]:
2     pass

其實際上的實現步驟為:

 1 # 首先獲得Iterator對象:
 2 it = iter([1, 2, 3, 4, 5])
 3 # 循環:
 4 while True:
 5     try:
 6         # 獲得下一個值:
 7         x = next(it)
 8     except StopIteration:
 9         # 遇到StopIteration就退出循環
10         break

Python之叠代器