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