自學Python4.8 - 生成器(方式二:生成器表達式)
定義:生成器(generator)是一個包含yield關鍵字的函數,當它被調用的時候,在函數體中的代碼不會被執行,而是會返回一個迭代器。
(一個函數調用時返回一個迭代器,那這個函數就叫作生成器(generator);
若是函數中包含yield語法,那這個函數就會變成生成器;)前端
- 生成器是一個特殊的程序,能夠被用做控制循環的迭代行爲
- 生成器相似於返回值爲數組的一個函數,這個函數能夠接收參數,能夠被調用,可是,不一樣於通常的函數會一次性返回包含了全部數值的數組,生成器一次 只產生一個值,這樣消耗的內粗數量大大減小,並且容許調用函數能夠很快的開始處理前幾個返回值。所以,生成器看起來像一個函數可是表現的卻像一個迭代器
python提供了兩種基本的生成器方式:python
- 生成器函數:也是用def來定義,利用關鍵字yield一次返回一個結果,阻塞,從新開始
每次請求一個值,就會執行生成器中的代碼,知道遇到一個yield或者return語句
①yield語句意味着應該生成一個值
②return語句意味着要中止執行(不生成任何東西,只有在一個生成器中使用時才能進行無參數調用)
- 生成器表達式:返回一個對象,這個對象只有在須要的時候才產生結果
2. 生成器表達式:
生成器表達式來自於迭代和列表解析的組合,生成器表達式和列表解析相似,可是他使用尖括號而不是方括號括起來的。數據庫
print([ x ** 3 for x in range(5)]) # 列表推導式
print((x ** 3 for x in range(5))) # 生成器表達式
print(list(x ** 3 for x in range(5)))# 二者之間轉換
輸出:django
![](http://static.javashuo.com/static/loading.gif)
for n in (x ** 3 for x in range(5)):
print('%s, %s' % (n, n * n)) # 就操做而言,生成器表若是使用大量的next()函數會顯得十分不方便,for循環會自動出發next函數
輸出:編程
![](http://static.javashuo.com/static/loading.gif)
[每個元素或者是和元素相關的操做 for 元素 in 可迭代數據類型] #遍歷以後挨個處理數組
[知足條件的元素相關的操做 for 元素 in 可迭代數據類型 if 元素相關的條件] #篩選功能網絡
舉例1:
names = [['Tom', 'Billy', 'Jefferson', 'Andrew', 'Wesley', 'Steven', 'Joe'],
['Alice', 'Jill', 'Ana', 'Wendy', 'Jennifer', 'Sherry', 'Eva']]
ret = [name for lst in names for name in lst if name.count('e') ==2]
print(ret)
![](http://static.javashuo.com/static/loading.gif)
字典推導式多線程
舉例1:併發
將一個字典的key和value對調
![](http://static.javashuo.com/static/loading.gif)
舉例2:
合併大小寫對應的value值,將k統一成小寫
![](http://static.javashuo.com/static/loading.gif)
舉例3:
集合推導式,自帶結果去重功能
![](http://static.javashuo.com/static/loading.gif)
3. 生成器函數與協程生成器表達式比較
一個迭代既能夠被寫成生成器函數,也能夠被協程生成器表達式,均支持自動和手動迭代。並且這些生成器只支持一個active迭代,也就是說生成器的迭代器就是生成器自己。
①
def recv():
print('Are your ready:')
while True:
n = yield #yield語句還有更給力的功能,做爲一個語句出如今賦值運算符的右邊,接受一個值,或同時生成一個值並接受一個值
print('總共用了 %s 秒' % n)
c = recv()
c.__next__()
c.send(100)
c.send(300)
輸出:
![](http://static.javashuo.com/static/loading.gif)
解釋:以上這種方式使用yield語句的函數稱爲協程。在這個例子中,對於__next__的初始調用是必不可少的,這樣協程才能執行可通向第一個yield表 達式的語句。在這裏協程會掛起,等待相關生成器對象send()方法給它發送一個值。傳遞給send()的值由協程中的yield表達式返回。 協程的運行通常是無限期的,使用方法close()能夠顯式的關閉它。
②
def split_line():
print('ready to split')
result = None
while True:
line = yield result #若是yield表達式中提供了值,協程可使用yield語句同時接收和發出返回值
result = line.split()
s = split_line()
s.__next__()
print(s.send('1,2,3'))
輸出:
![](http://static.javashuo.com/static/loading.gif)
解釋:
這個例子中的前後順序很是重要。首個next()方法讓協程執行到yield result,這將返回result的值None。
在接下來的send()調用中,接收到的值被放到line中並拆分到result中。
send()方法 的返回值就是下一條yield語句的值。也就是說,send()方法能夠將一個值傳遞給yield表達式,可是其返回值來自下一個yield表達式,而不是接收send()傳遞的值的yield表達式。
③ 若是想用send()方法來開啓協程的執行,必須先send一個None值,由於這時候是沒有yield語句來接受值的,不然就會拋出異常
def split_line():
print('ready to split')
result = None
while True:
line = yield result #若是yield表達式中提供了值,協程可使用yield語句同時接收和發出返回值
result = line.split()
s=split_line()
print(s.send('1 2 3'))
輸出:
![](http://static.javashuo.com/static/loading.gif)
def split_line():
print('ready to split')
result = None
while True:
line = yield result #若是yield表達式中提供了值,協程可使用yield語句同時接收和發出返回值
result = line.split()
s=split_line()
print(s.send(None))
print(s.send('1 2 3'))
輸出:
![](http://static.javashuo.com/static/loading.gif)
④ 生成器的功能很是強大。協程能夠用於實現某種形式的併發。在某些類型的應用程序中,能夠用一個任務調度器和一些生成器或協程實現協做式用戶空 間多線程,即greenlet。
yield的威力將在協程,協同式多任務處理(cooperative multitasking),以及異步IO中獲得真正的體現。
...