盤(pán)點(diǎn)那些不為人知的yield語(yǔ)法
2024-06-12 加入收藏
今天給大家?guī)?lái)比較硬核的知識(shí)點(diǎn),yield
的使用高階版。
yield用于生成器產(chǎn)生單個(gè)值
大多數(shù)小伙伴肯定是從生成器函數(shù)中第一次看到的yield關(guān)鍵字的吧?
沒(méi)錯(cuò),這是yield
最常用的場(chǎng)景。
def simple_gen():
yield 1
yield 2
yield 3
gen = simple_gen()
print(next(gen)) # 輸出 1
print(next(gen)) # 輸出 2
上面的代碼,yield
用于在生成器函數(shù)中產(chǎn)生單個(gè)值。
控制權(quán)返回給調(diào)用者,但保留生成器的狀態(tài),使得下次調(diào)用next()
時(shí)可以從上一次yield
的位置繼續(xù)執(zhí)行。
如果將生成器放入循環(huán)語(yǔ)句中yield
可多次產(chǎn)生值
def simple_gen():
yield 1
yield 2
yield 3
gen = simple_gen()
for i in gen:
print(i)
# 輸出 1 2 3
yield from + 委托生成器
如果我有多個(gè)生成需要組合成一個(gè)生成器,并希望有順序地輸出值該怎么辦?
def generator1():
yield 1
yield 2
def generator2():
yield 3
yield 4
我可以這樣寫(xiě)去實(shí)現(xiàn):
def generator1():
yield 1
yield 2
def generator2():
yield 3
yield 4
def combined_generator():
for i in generator1():
yield i
for i in generator2():
yield i
main_gen = combined_generator()
for i in main_gen:
print(i)
# 輸出 1 2 3 4
需要for循環(huán)才能實(shí)現(xiàn),有點(diǎn)難看...
引入yield from + 委托生成器之后進(jìn)行改寫(xiě):
yield from
是python3.3版本引入的。
def generator1():
yield 1
yield 2
def generator2():
yield 3
yield 4
####################
def combined_generator():
yield from generator1()
yield from generator2()
####################
main_gen = combined_generator()
for i in main_gen:
print(i)
# 輸出 1 2 3 4
改寫(xiě)后的代碼,yield from
用于在生成器函數(shù)(combined_generator)中委托部分生成任務(wù)給其他生成器(generator1,generator2), 而將子生成器的輸出直接傳遞給調(diào)用者,而不需要在主生成器中顯式循環(huán)子生成器的輸出。
更重要的是?。p少了循環(huán)的使用,減少了嵌套層次使得代碼更加簡(jiǎn)潔。前提是你已經(jīng)學(xué)會(huì)了這個(gè)語(yǔ)法。
有yield參與的賦值語(yǔ)句
先上代碼:
def generator_function():
value = yield "hello"
print(f"從yield接收的值是{value}")
yield value
有沒(méi)有感覺(jué)到一臉懵???為什么yield能用于賦值語(yǔ)句???
說(shuō)好的yield
和return
類似能在函數(shù)中返回一個(gè)值,怎么還能用于賦值語(yǔ)句???
我來(lái)強(qiáng)調(diào)下一件事:return
是返回后退出函數(shù),函數(shù)是被銷(xiāo)毀的。而yield
返回后是將函數(shù)掛起,函數(shù)還是存活的。
所以value = yield "hello"
這句執(zhí)行后,函數(shù)是被掛起的。
那從yield
那里接收的值是"hello"嗎?
不是的,從yield
中接收的值要從函數(shù)外部給它發(fā)送進(jìn)去。
完整代碼:
def generator_function():
value = yield "hello"
print(f"從yield接收的值是{value}")
yield value
g = generator_function()
first = next(g)
print(first)
second = g.send("world") # 向yield發(fā)送要接收的值
print(second)
當(dāng)send函數(shù)執(zhí)行后,會(huì)默認(rèn)對(duì)生成器執(zhí)行next方法到下一個(gè)返回/掛起點(diǎn)。
代碼執(zhí)行結(jié)果:
hello
從yield接收的值是world
world
yield實(shí)現(xiàn)一個(gè)狀態(tài)機(jī)
如果對(duì)上面的知識(shí)點(diǎn)能略懂略懂,那么就可以看下這段狀態(tài)機(jī)的代碼鞏固下知識(shí)點(diǎn)。
def vending_machine():
state = "idle"
while True:
if state == "idle":
print("售貨機(jī)空閑。插入硬幣以開(kāi)始售貨。")
coin = yield
if coin:
print("插入硬幣。切換到售貨狀態(tài)。")
state = "售貨中"
else:
print("未插入硬幣。保持在空閑狀態(tài)。")
elif state == "售貨中":
print("售貨機(jī)售貨中。按下按鈕以選擇商品。")
selection = yield
if selection:
print(f"售貨商品 {selection}。返回到空閑狀態(tài)。")
state = "空閑"
else:
print("未選擇商品。繼續(xù)售貨。")
# 創(chuàng)建狀態(tài)機(jī)生成器對(duì)象
vm_gen = vending_machine()
# 啟動(dòng)狀態(tài)機(jī)
next(vm_gen)
# 模擬用戶操作
vm_gen.send(True) # 插入硬幣,切換到售貨狀態(tài)
vm_gen.send("蘋(píng)果汁") # 選擇商品,返回到空閑狀態(tài)
GPT對(duì)代碼解釋:
vending_machine 函數(shù)定義了一個(gè)狀態(tài)機(jī),有兩個(gè)狀態(tài):"idle"(空閑)和 "vending"(售貨中)。 通過(guò) while True 實(shí)現(xiàn)一個(gè)無(wú)限循環(huán),保持狀態(tài)機(jī)的運(yùn)行。 在 "idle" 狀態(tài)下,狀態(tài)機(jī)等待硬幣的插入。如果收到硬幣,狀態(tài)切換到 "vending"。 在 "vending" 狀態(tài)下,狀態(tài)機(jī)等待用戶按下按鈕選擇商品。如果選擇了商品,狀態(tài)切換回 "idle"。 通過(guò) yield 實(shí)現(xiàn)暫停和恢復(fù)狀態(tài)機(jī)的執(zhí)行。yield 語(yǔ)句在產(chǎn)生值的同時(shí)也接收到來(lái)自外部的輸入。 在主程序中,我們創(chuàng)建了狀態(tài)機(jī)生成器對(duì)象 vm_gen,并通過(guò)調(diào)用 next(vm_gen) 啟動(dòng)了狀態(tài)機(jī)。然后,通過(guò) vm_gen.send(True) 模擬了插入硬7. 幣的操作,再通過(guò) vm_gen.send("蘋(píng)果汁") 模擬了選擇商品的操作。
如果還是對(duì)這段代碼存疑,最好再使用編輯器的debug跑一下一段代碼,觀察yield的狀態(tài)(作用)。