yield from 是 Python3.3 後新加的語言結構。yield from的主要功能是打開雙向通道,把最外層的調用方法與最內層的子生成器鏈接起來。這二者就能夠進行發送值和返回值了,yeild from結構的本質是簡化嵌套的生產器,不理解這個是什麼意思的話,下面我將用幾個例子來對其使用方法進行講解。python
首先看一個git
def gene():
for c in 'AB':
yield c #遇到yeild程序返回循環,下次從yeild後面開始。
for i in range(3):
yield i
if __name__=="__main__":
list(gene())#list內部會預激生成器
複製代碼
輸出github
['A','B','0','1', '2']
複製代碼
上面的代碼能夠簡寫成bash
def gene():
yield from 'ab'
yield from range(3)
if __name__=="__main__":
list(gene())
複製代碼
經過上面的代碼咱們能夠知道,yield from 能夠簡化for循環裏的yield表達式。固然yeild from的功能不單單是能夠簡化for循環而已,要是這樣的話也就不值得,單獨寫一篇文章來介紹了。網絡
咱們仔細觀察,簡化後的式子有兩個yeild from,一樣的也就是說若是有10個for循環的yeild生成式,咱們須要寫10個yeild from,此時咱們要記得在python中若是重複的代碼出現了兩次以及以上就該考慮優化了。好了接下來咱們看一個優化後的例子。app
def chain(*args):
for i in args:
# for m in i:
# yield m
yield from i
p = list(chain("1234", "AB", [1, 2, 3, 4, 5]))
print(p)
複製代碼
輸出異步
['1', '2', '3', '4', 'A', 'B', 1, 2, 3, 4, 5]
複製代碼
這裏對以前的例子作了個優化處理,經過*args可變參數,配合後面的for循環進行了多個可迭代對象的連接處理。下面來看一個複雜點的例子:(來自Python cookbook 3 ,github源碼地址 https://github.com/dabeaz/python-cookbook/blob/master/src/4/how_to_flatten_a_nested_sequence/example.py)函數
# Example of flattening a nested sequence using subgenerators
from collections import Iterable
def flatten(items, ignore_types=(str, bytes)):
for x in items:
if isinstance(x, Iterable) and not isinstance(x, ignore_types):
yield from flatten(x)
else:
yield x
items = [1, 2, [3, 4, [5, 6], 7], 8]
# Produces 1 2 3 4 5 6 7 8
for x in flatten(items):
print(x)
items = ['Dave', 'Paula', ['Thomas', 'Lewis']]
for x in flatten(items):
print(x)
複製代碼
接下來經過說一下開篇提到的子生產器和調用方以及新的詞委託生成器。fetch
yield from x 表達式對x對象作的第一件事是,調用 iter(x),從中獲取一個迭代器。因此x是可迭代對象。上面的例子中的x若是是可迭代對象就會執行,yield from flatten(x).優化
PEP380 的標題是 」syntax for delegating to subgenerator「(把指責委託給子生成.器的句法)。由此咱們能夠知道,yield from是能夠實現嵌套生成器的使用。
yield from在看接下來的代碼以前咱們必須知道這幾個概念:
包含yield from 表達式的生成器函數
從yield from 部分獲取的生成器,含義yield的。
調用委派生成器的客戶端(調用方)代碼,也就是運行入口。
ok,瞭解了這些咱們看接下來的一個例子。
import requests
from collections import namedtuple ①
Response = namedtuple("rs", 'url status') ②
# 子生產器
def fecth(): ③
res=[]
while 1:
url = yield ④
if url is None: ⑤
break
req = requests.get(url)
res.append(Response(url=url, status=req.status_code))
return res
#委派生成器
def url_list(l, key):
while 1: ⑥
l[key] = yield from fecth() ⑦
#調用方
def main():
l = {}
u = ["http://www.baidu.com", "http://www.cnblogs.com"]
for index, url in enumerate(u):
if index == 0:
ul = url_list(l, index)
next(ul) ⑧
ul.send(url)⑨
ul.send(None)⑩
return l
if __name__ == '__main__':
res = main()
print(res)
複製代碼
接下來對上面的標準進行解釋:
① 引入一個具名元組,能夠後面實現一個簡單的類。
② 對請求參數作一個格式化處理,後面經過獲取屬性便可。
③一個協程,經過requests模塊能夠發起網絡請求。
④main函數的發送的值綁定到這裏的url上
⑤ url爲None即沒有url的時候結束循環的。
⑥這個循環每次都會新建一個fetch 實例,每一個實例都是做爲協程使用的生成器對象。
⑦ url_list發送的每一個值都會經由yield from 處理,而後傳給fetch 實例。url_list會在yield from表達式處暫停,等待fetch實例處理客戶端發來的值。fetch實例運行完畢後,返回的值綁定到l[key] 上。while 循環會不斷建立fetch實例,處理更多的值。
⑧激活url_list生成器
⑨把各個url以及其序列號index,傳給url_list傳入的值最終到達fetch函數中,url_list並不知道傳入的是什麼,同時url_list實例在yield from處暫停。直到fetch的一個實例處理完才進行賦值。
⑩關鍵的一步,# 把None傳入url_list,傳入的值最終到達fetch函數中,致使當前實例終止。而後繼續建立下一個實例。若是沒有ul.send(None),那麼fetch子生成器永遠不會終止,由於ul.send()發送的值實際是在fetch實例中進行,委派生成器也永遠不會在此激活,也就不會爲l[key]賦值
流暢的python 第16章 PEP 380-- Syntax for Delegating to a Subgenerator How Python 3.3 "yield from" construct works