因為水平有限兑徘,我在看asyncio異步爬蟲的時候刚盈,關于async/await的相關知識,看了挺多資料挂脑,還是不得其要領藕漱,所以用yield、yields from到async\await從頭捋搞了一遍崭闲,發(fā)現(xiàn)里邊得東西還是挺有意思肋联,所以搞了這么一篇文章。
0.yield
yield
可以將一個函數(shù)轉換成 generator(生成器)
刁俭,和普通函數(shù)不同得是橄仍,生成一個 generator
看起來像函數(shù)調用,但不會執(zhí)行任何函數(shù)代碼牍戚,直到對其調用 next()
/send()
才開始執(zhí)行侮繁,雖然執(zhí)行流程仍按函數(shù)得流程執(zhí)行,但是執(zhí)行到每一個yield
語句就會中斷如孝,交出執(zhí)行權宪哩,并返回一個迭代值,下次執(zhí)行從yield
中斷處可以通過send()
接收一個參數(shù)并繼續(xù)執(zhí)行第晰∷希看起來就像一個函數(shù)在正常執(zhí)行得過程中被yield
中斷了數(shù)次,每次中斷都會交出執(zhí)行權但荤,通過 yield
返回當前的迭代值罗岖。
yield
的好處是顯而易見的,把一個函數(shù)改寫為一個 generator
就獲得了迭代能力腹躁,這也是python中協(xié)程的基礎桑包。
0.1 next()
next()
的作用是每次獲取一個yield出來的值。
def generator():
yield 1
yield 2
yield 3
return 4
if __name__ == "__main__":
gen = generator()
print(next(gen))
print(next(gen))
print(next(gen))
print(next(gen)) # 因為不是yield纺非,這里拋出一個StopIteration的錯誤哑了,但錯誤信息中會返回 4。 錯誤詳情:StopIteration: 4
gen.close()
0.2 send()
send()
和next()
類似烧颖,但是比next()多一個功能弱左,可以往生成器中傳入?yún)?shù)。
def generator2():
num = yield 1
print (num)
yield 2
yield 3
return 4
if __name__ == "__main__":
gen = generator2()
print(gen.send(None)) # 如果使用send()的時候炕淮,第一個傳入的值必須是None拆火,因為當調用send()的時候,才執(zhí)行到第一個yield,只能輸出一個值们镜,不能接收值币叹。
print(gen.send(101))
print(gen.send(None))
gen.close()
注:關于StopIteration:1. 在一個生成器中,如果沒有return模狭,則默認執(zhí)行到函數(shù)完畢時返回StopIteration颈抚;2. 如果遇到return,如果在執(zhí)行過程中 return,則直接拋出 StopIteration 終止迭代嚼鹉。3.在close()之后贩汉,如果再執(zhí)行相關的操作,會拋出StopIteration锚赤;
0.3 throw()
throw()
用來向生成器函數(shù)送入一個異常匹舞,可以結束系統(tǒng)定義的異常,或者自定義的異常线脚。我的理解是相當于send()進去一個錯誤類型策菜,然后拋出異常。我的理解可能不準確酒贬,僅供參考又憨。
def generator3():
num = yield 1
print(num)
try:
yield 2
except ValueError:
print("捕獲異常:ValueError")
yield 3
if __name__ == "__main__":
gen = generator3()
print(gen.send(None))
print(gen.send(101))
gen.throw(ValueError)
gen.close()
1.yield from
我們先來看個例子:
def test03(iterabld):
yield iterabld
def test04(iterable):
yield from iterable
if __name__ == "__main__":
for iterabld in test03(range(5)):
print(iterabld)
for value in test04(range(5)):
print (value)
返回值:
# range(0, 5)
# 0
# 1
# 2
# 3
# 4
這兩個的返回值是一樣的,可以看的出來锭吨,yield form 將可迭代對象中的值直接迭代出來了蠢莺。下邊再看一個例子:
def generator01():
num = yield 1
print(num)
yield 2
yield 3
return 4
def test01(gen):
ll = yield from gen()
print(ll)
def main():
gen = test01(generator01)
print(gen.send(None))
print(gen.send(101))
print(gen.throw(ValueError))
if __name__ == "__main__":
main()
執(zhí)行下例子,可以發(fā)現(xiàn)零如,再main()中的send()和throw()都是直接作用
到生成器generator01中的躏将。這其中main()函數(shù)被稱作調用方
,test01()被稱作委托生成器
(包含yield from表達式的生成器函數(shù))考蕾,generator01()被稱作子生成器
(yield from后面加的生成器函數(shù))祸憋。
委托生成器的作用是:在調用方與子生成器之間建立一個雙向通道
,拿例子來講肖卧,就是main()中的send()等直接作用到generator01到蚯窥,而generator01中yield產生的值直接返回到main()中。
委托生成器中:ll = yield from gen()
為啥會賦值塞帐? 這是接收的return的值拦赠。生成器沒有yield,有return的時候葵姥,會拋出StopIteration異常荷鼠,在拋出StopIteration的異常的時候,會將return的值賦給ll榔幸。
注:1. yield from 是在Python3.3才出現(xiàn)的語法允乐。所以這個特性在Python2中是沒有的矮嫉。 2.yield from 后面需要加的是可迭代對象。它可以是普通的可迭代對象牍疏,也可以是迭代器敞临、生成器。
2. async/await
async/await 是用是python3.5后出來的協(xié)程異步編程的API, 是為了區(qū)分yield麸澜,yield from生成器,而使語義更加明確奏黑。以下示例代碼只是為了展示await與yield from對比炊邦,實際開發(fā)中,不要這么做熟史!不要這么做D俸Α!蹂匹!
# 示例代碼一
import requests
async def request(url):
return requests.get(url)
async def spider(url):
return await request(url)
if __name__ == '__main__':
sp = spider("http://www.baidu.com")
try:
print(sp.send(None))
except StopIteration as e:
print (e.value)
# 示例代碼二
import requests
import types
@types.coroutine
def request(url):
yield requests.get(url)
async def spider(url):
return await request(url)
if __name__ == '__main__':
sp = spider("http://www.baidu.com")
print(sp.send(None))
# 以上代碼的打印結果都是 <Response [200]>
async def
語法定義協(xié)程函數(shù)
碘菜,在之前這個功能是通過裝飾器實現(xiàn)的。但是這樣定義的協(xié)程函數(shù)中不能使用yield語句
限寞,只允許使用return或await語句返回數(shù)據(jù)忍啸。
await
的使用場景與yield from
類似,但是await
接收的對象不同履植。yield from
可以是任意的可迭代對象计雌。而await
接收的對象必須是可等待對象(awaitable object)
注:1. async/await是在python3.5版么以及之后的版本中才能使用。2. async不能和yield同時使用玫霎。3.await只能作用于可等待對象
推薦閱讀:https://www.cnblogs.com/harelion/p/8496360.html