Python中yield函數(shù)的用法詳解
1. yield介紹
在Python中,yield關(guān)鍵字主要用于生成器函數(shù)(generator functions)中,其目的是使函數(shù)能夠像迭代器一樣工作,即可以被遍歷,但不會一次性將所有結(jié)果都加載到內(nèi)存中。
2.yield基本用法
- 定義生成器函數(shù)
def simple_generator(): yield 1 yield 2 yield 3 gen = simple_generator() print(next(gen)) # 輸出: 1 print(next(gen)) # 輸出: 2 print(next(gen)) # 輸出: 3
- 使用 for 循環(huán)遍歷生成器生成器對象是可迭代的,因此可以使用 for 循環(huán)來遍歷生成器生成的值。
下面例子中,for 循環(huán)遍歷生成器函數(shù)生成的所有值,并依次打印它們。
def simple_generator(): yield 1 yield 2 yield 3 for value in simple_generator(): print(value)
- 生成器表達(dá)式下面例子中,生成器表達(dá)式生成了一個平方數(shù)序列,并使用 for 循環(huán)打印所有值。
gen_expr = (x * x for x in range(5)) for value in gen_expr: print(value)
- 生成器轉(zhuǎn)列表
def simple_generator(): yield 1 yield 2 yield 3 gen = simple_generator() print(list(gen)) # 輸出: [1, 2, 3]
3.yield高級用法
3.1 yield send() 方法
生成器不僅可以通過 yield
返回值,還可以通過 send()
方法接收外部輸入。
send()
方法允許向生成器發(fā)送一個值,這個值將成為上一個 yield 表達(dá)式的值。
這種方式可以實現(xiàn)生成器與外部之間的雙向通信。
def coroutine(): while True: received = yield print(f"接收到的數(shù)據(jù): {received}") co = coroutine() next(co) # 預(yù)激生成器 co.send(10) # 輸出: 接收到的數(shù)據(jù): 10 co.send(20) # 輸出: 接收到的數(shù)據(jù): 20
在這個例子中,coroutine
生成器函數(shù)在每次迭代時接收外部數(shù)據(jù)并打印它。next(co)
用于預(yù)激生成器,使其準(zhǔn)備好接收數(shù)據(jù)。
3.2 yield from方法
從Python 3.3開始,引入了 yield from
語法,它允許一個生成器委托另一個生成器來生成值。
這種委托機(jī)制可以簡化嵌套生成器的使用,提高代碼的可讀性和效率。
def base_code_pool(): for i in range(3): yield f'BASE-{i + 1}' def outsource_pool(): for i in range(30): yield f'OUTS-{i + 1}' def team_member_code(): yield from base_code_pool() print('內(nèi)部資源編號用完,開始使用外包') yield from outsource_pool() team_member = team_member_code() for i in range(5): print(next(team_member))
運行結(jié)果:
BASE-1
BASE-2
BASE-3
內(nèi)部資源編號用完,開始使用外包
OUTS-1
OUTS-2
在這個例子中,team_member_code
生成器函數(shù)委托 base_code_pool
和 outsource_pool
生成器來生成值。
當(dāng) base_code_pool
的值用完后,生成器會繼續(xù)從 outsource_pool
生成值。
3.3 yield 和yield from疊加
yield
和 yield from
是 Python 中用于創(chuàng)建生成器的兩種方式,它們允許函數(shù)在迭代過程中逐步返回值,而不是一次性返回所有結(jié)果。
這種特性使得生成器非常適合處理大型數(shù)據(jù)集或無窮序列等場景,因為它們不會一次性將所有數(shù)據(jù)加載到內(nèi)存中,從而節(jié)省了內(nèi)存資源。
關(guān)于是否可以“疊加”yield 和 yield from 的結(jié)果,實際上我們討論的是如何組合多個生成器或者將一個生成器的結(jié)果傳遞給另一個生成器。
- 使用 yield 和 yield from 組合生成器當(dāng)你想要組合兩個或更多個生成器時,你可以使用
yield
來逐個產(chǎn)出每個生成器中的元素,或者使用yield from
來直接委托給子生成器,讓其負(fù)責(zé)產(chǎn)出自己的元素。 - 例如:
def generator_a(): for i in range(3): yield i def generator_b(): for i in range(3, 6): yield i def combined_generators(): # 使用 yield 直接產(chǎn)出每個生成器中的元素 for value in generator_a(): yield value for value in generator_b(): yield value # 或者使用 yield from 委托給子生成器 yield from generator_a() yield from generator_b()
在這個例子中,combined_generators
函數(shù)通過 yield
和 yield from
將兩個生成器的結(jié)果進(jìn)行了“疊加”。
這里,“疊加”的含義是指順序地連接了兩個生成器產(chǎn)生的輸出流,而不是數(shù)學(xué)意義上的加法操作。
可以將 yield
和 yield from
結(jié)合使用來實現(xiàn)生成器之間的組合,甚至可以在同一個函數(shù)內(nèi)部同時使用這兩種語句。
這樣做不僅可以簡化代碼結(jié)構(gòu),還能更靈活地控制生成器的行為。
讓我們深入探討一下如何有效地結(jié)合使用 yield
和 yield from
來“疊加”多個生成器的結(jié)果。
- 結(jié)果的實際疊加如果我們談?wù)摰氖菍嶋H的結(jié)果疊加(如數(shù)值相加),那么你需要明確地編寫邏輯來實現(xiàn)這一點。
比如,如果你想把來自不同生成器的數(shù)值相加以獲得總和,你可以這樣做:
def sum_of_generators(gen1, gen2): return sum(gen1) + sum(gen2) total = sum_of_generators(generator_a(), generator_b()) print(total) # 輸出: 15 (0+1+2+3+4+5) # 輸出: 0 1 2 3 4 5 for item in combined_generators(): print(item)
在這里,sum_of_generators
函數(shù)接收兩個生成器作為參數(shù),并分別對它們求和后再相加。
請注意,這種方法會立即消耗掉這兩個生成器的所有元素并計算出總和,這可能不是最有效的做法,特別是對于非常大的數(shù)據(jù)集。
- 組合使用 yield 和 yield from上面的例子可以將
yield
和yield from
結(jié)合使用來實現(xiàn)生成器之間的組合,甚至可以在同一個函數(shù)內(nèi)部同時使用這兩種語句。
這樣做不僅可以簡化代碼結(jié)構(gòu),還能更靈活地控制生成器的行為。
讓我們深入探討一下如何有效地結(jié)合使用yield
和yield from
來“疊加”多個生成器的結(jié)果。
考慮一個場景,你有一個主生成器,它需要從多個子生成器中獲取值,并且自身也可能產(chǎn)生一些額外的值。
在這種情況下,你可以用yield
來直接產(chǎn)出這些額外的值,而用yield from
來委派給子生成器。
例如:
def generator_a(): for i in range(3): yield i def generator_b(): for i in range(3, 6): yield i def combined_generators(): # 直接使用 yield 產(chǎn)出額外的值 yield "Start" # 使用 yield from 委托給子生成器 a yield from generator_a() # 再次直接使用 yield 產(chǎn)出中間的值 yield "Middle" # 使用 yield from 委托給子生成器 b yield from generator_b() # 最后直接使用 yield 產(chǎn)出結(jié)束的值 yield "End" # 輸出: Start 0 1 2 Middle 3 4 5 End for item in combined_generators(): print(item)
在這個例子中,combined_generators
函數(shù)展示了如何在同一個生成器內(nèi)混合使用 yield
和 yield from
。
首先,它通過 yield
發(fā)出了字符串 "Start"
;
然后,利用 yield from
將控制權(quán)交給 generator_a()
,后者負(fù)責(zé)產(chǎn)出 0, 1, 和 2
;
接著,再次使用 yield
發(fā)出 "Middle"
;之后,又通過 yield from
讓 generator_b()
產(chǎn)出 3, 4, 和 5
;
最后,以同樣的方式發(fā)出字符串 "End"
。
這種方式允許你在不破壞原有邏輯的基礎(chǔ)上輕松添加新的生成邏輯,同時也保持了代碼的清晰度和可讀性。
處理復(fù)雜情況下的疊加
如果你面對的是更加復(fù)雜的場景,比如你需要對來自不同生成器的數(shù)據(jù)進(jìn)行某種形式的處理后再輸出,或者你需要根據(jù)某些條件決定是否調(diào)用某個子生成器,那么你可以繼續(xù)擴(kuò)展這種模式。
例如,假設(shè)你想把兩個生成器的結(jié)果相加后作為最終輸出的一部分:
def add_generators(gen1, gen2): iterator1 = iter(gen1) iterator2 = iter(gen2) try: while True: value1 = next(iterator1) value2 = next(iterator2) yield value1 + value2 except StopIteration: pass def enhanced_combined_generators(): yield "Start" yield from add_generators(generator_a(), generator_b()) yield "End" # 輸出: Start 3 5 7 End for item in enhanced_combined_generators(): print(item)
綜上所述,yield
和 yield from
的結(jié)果可以通過編程邏輯被“疊加”,但這取決于你想要實現(xiàn)的具體行為。
如果是簡單的迭代輸出,則可以直接使用 yield from 來簡化代碼;
而如果是數(shù)值或其他類型的累加,則需要額外的邏輯來完成這個過程。
4.yield主要應(yīng)用場景
- 處理大數(shù)據(jù)集生成器非常適合處理大數(shù)據(jù)集,因為它可以在需要時按需生成值,而不是一次性將所有值加載到內(nèi)存中。
這可以顯著減少內(nèi)存使用,提高程序的性能。
def read_large_file(file_path): with open(file_path, 'r') as file: for line in file: yield line for line in read_large_file('large_file.txt'): print(line, end='')
在這個例子中,生成器函數(shù) read_large_file
逐行讀取大文件,并使用 for 循環(huán)打印每行內(nèi)容。
這種方法避免了將整個文件加載到內(nèi)存中,從而節(jié)省了內(nèi)存。
- 實現(xiàn)協(xié)程和并發(fā)生成器可以用于實現(xiàn)協(xié)程和并發(fā)編程模式。
協(xié)程是一種用戶態(tài)的輕量級線程,可以用來模擬異步操作,實現(xiàn)非阻塞的I/O處理等。
def coroutine_example(): while True: received = yield print(f"處理數(shù)據(jù): {received}") co = coroutine_example() next(co) # 預(yù)激生成器 co.send('data1') # 輸出: 處理數(shù)據(jù): data1 co.send('data2') # 輸出: 處理數(shù)據(jù): data2
在這個例子中, coroutine_example
生成器函數(shù)可以接收外部數(shù)據(jù)并處理它,實現(xiàn)了簡單的協(xié)程功能。
- 數(shù)據(jù)處理管道生成器可以用于實現(xiàn)數(shù)據(jù)處理管道,每個生成器函數(shù)負(fù)責(zé)一個處理步驟。
這種模式可以將復(fù)雜的任務(wù)分解為多個簡單的步驟,提高代碼的可讀性和可維護(hù)性。
def pipeline_stage1(data): for item in data: yield item * 2 def pipeline_stage2(data): for item in data: yield item + 1 data = range(5) stage1 = pipeline_stage1(data) stage2 = pipeline_stage2(stage1) for value in stage2: print(value)
在這個例子中,數(shù)據(jù)經(jīng)過兩個生成器函數(shù)處理。
首先在 pipeline_stage1
中乘以2,然后在 pipeline_stage2
中加1。
def add_generators(gen1, gen2): iterator1 = iter(gen1) iterator2 = iter(gen2) try: while True: value1 = next(iterator1) value2 = next(iterator2) yield value1 + value2 except StopIteration: pass def enhanced_combined_generators(): yield "Start" yield from add_generators(generator_a(), generator_b()) yield "End" # 輸出: Start 3 5 7 End for item in enhanced_combined_generators(): print(item) 這里定義了一個輔助函數(shù) add_generators,它接受兩個生成器并逐個取出它們的元素相加以生成新的值。enhanced_combined_generators 則是在之前的基礎(chǔ)上加入了這個新功能。通過這種方式,你可以非常靈活地構(gòu)建復(fù)雜的生成器邏輯,而不需要為每一個可能的變化都編寫全新的生成器10。
綜上所述,yield 和 yield from 可以很好地協(xié)同工作,幫助開發(fā)者構(gòu)建高效、易于維護(hù)的生成器代碼。無論是簡單的串聯(lián)還是更復(fù)雜的操作,如數(shù)據(jù)變換或條件分支,都可以通過合理的設(shè)計達(dá)到預(yù)期的效果。重要的是要理解每種語句的作用以及它們?nèi)绾蜗嗷プ饔?,這樣才能寫出既強(qiáng)大又優(yōu)雅的 Python 代碼2。此外,yield from 的引入不僅簡化了代碼,還增強(qiáng)了生成器之間通信的能力,特別是在涉及到異常傳遞時顯得尤為重要14。因此,在實際編程實踐中,充分利用這兩種工具能夠極大地提升代碼的質(zhì)量和性能。
5.總結(jié)
- yield函數(shù)屬性
- 當(dāng)一個函數(shù)包含yield關(guān)鍵字時,這個函數(shù)就變成了一個生成器函數(shù)。
- 調(diào)用生成器函數(shù)并不會立即執(zhí)行函數(shù)體內(nèi)的代碼,而是返回一個生成器對象。
- 生成器對象可以被迭代,每次迭代時,生成器函數(shù)會從上次離開的地方繼續(xù)執(zhí)行,直到遇到下一個yield語句,然后返回一個值,并保存當(dāng)前狀態(tài),以便下次繼續(xù)執(zhí)行。
- 這種行為使得生成器非常適合處理大量數(shù)據(jù)或流式數(shù)據(jù),因為它不需要一次性將所有數(shù)據(jù)加載到內(nèi)存中,從而節(jié)省了內(nèi)存資源。
- yield vs return
return
:當(dāng)函數(shù)執(zhí)行到return
語句時,它會立即停止執(zhí)行,并返回一個值給調(diào)用。
這意味著函數(shù)的局部變量會被銷毀,且函數(shù)的執(zhí)行狀態(tài)不會被保存。
因此,如果再次調(diào)用同一個函數(shù),它將從頭開始執(zhí)行。yield
:與return
不同,當(dāng)執(zhí)行到y(tǒng)ield語句時,函數(shù)會暫停執(zhí)行,返回一個值給調(diào)用者,并保存當(dāng)前的所有局部變量狀態(tài)。
下次調(diào)用生成器時,函數(shù)將從上次暫停的地方恢復(fù)執(zhí)行,直到遇到下一個yield語句或函數(shù)結(jié)束。
- 生成器的使用場景
- 處理大數(shù)據(jù)集:由于生成器是惰性求值的,它可以在需要時才生成數(shù)據(jù),因此非常適合處理大數(shù)據(jù)集,避免了一次性加載所有數(shù)據(jù)導(dǎo)致的內(nèi)存不足問題。
- 簡化代碼結(jié)構(gòu):使用生成器可以簡化代碼結(jié)構(gòu),尤其是當(dāng)需要處理復(fù)雜的狀態(tài)機(jī)或遞歸結(jié)構(gòu)時。
- 協(xié)程與并發(fā):雖然Python的標(biāo)準(zhǔn)庫中已經(jīng)提供了多種并發(fā)模型,但生成器可以作為一種輕量級的協(xié)程實現(xiàn),用于實現(xiàn)簡單的并發(fā)任務(wù)。
到此這篇關(guān)于Python中yield函數(shù)用法詳解的文章就介紹到這了,更多相關(guān)Python yield函數(shù)內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

Python?eval()和exec()函數(shù)使用詳解

Anaconda+spyder+pycharm的pytorch配置詳解(GPU)

實例探究Python以并發(fā)方式編寫高性能端口掃描器的方法