python中,yield關鍵字的作用:1、將一個函數修改為生成器,利用生成器可以有效地節省系統資源,避免不必要的記憶體佔用;2、用於定義上下文管理器;3、協程;4、配合from形成yield from用於消費子產生器並傳遞訊息。
yield 的用法有以下四個常見的情況:
-
一個是生成器,
概括的話就是:生成器內部的程式碼執行到yield會返回,返回的內容為yield後的表達式。下次再執行生成器的內部程式碼時將從上次的狀態繼續開始。透過yield關鍵字,我們可以很方便的將一個函數修改為生成器。
二是用來定義上下文管理器,
#三是協程,
四是配合from 形成yield from 用於消費子產生器並傳遞訊息。
這四種用法,其實都源自於yield 所具有的暫停的特性,也就說程式在運行到yield 所在的位置result = yield expr 時,先執行yield expr將產生的值傳回給呼叫產生器的caller,然後暫停,等待caller 再次啟動並恢復程式的執行。而根據復原程式所使用的方法不同,yield expr 運算式的結果值 result 也會跟著變化。如果使用 __next()__ 來調用,則 yield 表達式的值 result 是 None;如果使用 send() 來調用,則 yield 表達式的值 result 是透過 send 函數傳送的值。以下是官方文件介紹yield 表達式時的一個例子[1],能夠很好地說明關鍵字 yield 的特性和用法:
>>> def echo(value=None): ... print("Begin...") ... try: ... while True: ... try: ... value = (yield value) ... except Exception as e: ... value = e ... finally: ... print("Clean up!!!") ... >>> generator = echo(1) >>> print(next(generator)) Begin... 1 >>> print(next(generator)) None >>> print(generator.send(2)) 2 >>> generator.throw(TypeError, "spam") TypeError('spam') >>> generator.close() Clean up!!!
上面這段程式碼的說明如下圖所示:
執行第一個next(generator) 的時候,也就是預啟動產生器,生成器開始執行,列印Begin... 字串,執行到value = (yield value) 的位置時,首先呼叫yield value 產生數字1,然後生成器在yield 的位置暫停。
接著呼叫第2 個next(generator) 的時候,生成器恢復執行,由於使用next() 來呼叫生成器函數, value 的值會變成None ,因此生成當器函數繼續執行到yield value 時,會將value 的值None 傳回給解釋器,然後再次暫停。
接著使用 send(2) 方法繼續呼叫產生器,value 接收到傳入的數位 2,並繼續到執行 value = (yield value) ,將數位 2 回傳給解譯器後暫停。
此後,解釋器再次透過throw(TypeError, "spam") 方法調用,生成器恢復執行,並拋出異常,生成器捕獲到異常,並將異常TypeError( 'spam') 賦值給變數value,然後程式再次執行到value = (yield value) ,將TypeError('spam') 回傳給解釋器。
最後,程式呼叫 close() 方法,在產生器函數的位置拋出 GeneratorExit ,異常被拋出,產生器正常退出,並最終執行最外層try 語句對應的finally 分支,列印輸出 Clean up。
python中有一個非常有用的語法叫做生成器,所利用的關鍵字就是yield。有效利用生成器這個工具可以有效地節省系統資源,避免不必要的記憶體佔用。
生成器
不出意外,你最先遇到 yield 一定會是一個生成器函數裡面。生成器是一個用於不斷產生數字或其他類型的值的函數,可以透過 for 迴圈或 next() 函數逐一呼叫。這裡要強調的是,生成器包含的是一個沒有賦值的yield 表達式,所以下面兩種形式是等價的[2]:
def integers_1(): for i in range(4): yield i + 1def integers_2(): for i in range(4): value = yield i + 1
這裡之所以強調第二種形式,是為了在理解透過send() 方法發送value 時,能夠更好地理解yield。同時,也能夠更正確地說明,呼叫生成器傳回的值是 yield 關鍵字右邊的表達式 i 1 的值,而不是 yield 表達式本身的結果值。
我們試著呼叫一下:
>>> for n in integers_1(): ... print(n) ... 1 2 3 4 >>> for n in integers_2(): ... print(n) ... 1 2 3 4
上下文管理器
配合Python 的contexlib 模組裡的@contextmanager 裝飾器,yield 也可以用於定義上下文管理器,以下是Python Tricks 書中的一個例子[3]:
from contextlib import contextmanager @contextmanager def managed_file(name): try: f = open(name, 'w') yield f finally: f.close()
上面透過裝飾器和yield 關鍵字定義的上下文管理器和下面類別的方法定義等同:
class ManagedFile: def __init__(self, name): self.name = name def __enter__(self): self.file = open(self.name, 'w') return self.file def __exit__(self, exc_type, exc_val, exc_tb): if self.file: self.file.close()
可以利用下面的方法分別進行呼叫:
>>> with ManagedFile('hello.txt') as f: ... f.write('hello, world!') ... f.write('bye now') >>> with managed_file('hello.txt') as f: ... f.write('hello, world!') ... f.write('bye now')
協程
協程的概念充滿了美感,非常符合人的辦事模式,想要完全掌握卻還是需要花費一些功夫。不過這些功夫是值得的,因為有時多線程所帶來的麻煩會遠遠比協程多。下面是 Python Cookbook 中的一個只用 yield 表達式寫的協程實例[4]:
from collections import deque # Two simple generator functions def countdown(n): while n > 0: print('T-minus', n) yield n -= 1 print('Blastoff!') def countup(n): x = 0 while x < n: print('Counting up', x) yield x += 1 class TaskScheduler: def __init__(self): self._task_queue = deque() def new_task(self, task): ''' Admit a newly started task to the scheduler ''' self._task_queue.append(task) def run(self): ''' Run until there are no more tasks ''' while self._task_queue: task = self._task_queue.popleft() try: # Run until the next yield statement next(task) self._task_queue.append(task) except StopIteration: # Generator is no longer executing pass # Example use sched = TaskScheduler() sched.new_task(countdown(2)) sched.new_task(countup(5)) sched.run()
运行上面的脚本,可以得到以下输出:
T-minus 2 Counting up 0 T-minus 1 Counting up 1 Blastoff! Counting up 2 Counting up 3 Counting up 4
countdown 和 countup 两个任务交替执行,主程序在执行到 countdown 函数的 yield 表达式时,暂停后将被重新附加到队列里面。然后,countup 任务从队列中取了出来,并开始执行到 yield 表达式的地方后暂停,同样将暂停后的协程附加到队列里面,接着从队列里取出最左边的任务 countdown 继续执行。重复上述过程,直到队列为空。
上面的协程可以利用 Python3.7 中的 asyncio 库改写为:
import asyncio async def countdown(n): while n > 0: print('T-minus', n) await asyncio.sleep(0) n -= 1 print('Blastoff!') async def countup(n): x = 0 while x < n: print('Counting up', x) await asyncio.sleep(0) x += 1 async def main(): await asyncio.gather(countdown(2), countup(5)) asyncio.run(main())
可以看到利用 asyncio 库编写的协程示例比用 yield 来编写的协程要优雅地多,也简单地多,更容易被人理解。
yield from
说实话,yield from 实在有点令人费解,让人摸不着头脑。yield from 更多地被用于协程,而 await 关键字的引入会大大减少 yield from 的使用频率。yield from 一方面可以迭代地消耗生成器,另一方面则建立了一条双向通道,可以让调用者和子生成器便捷地通信,并自动地处理异常,接收子生成器返回的值。下面是 Python Cookbook 书里的一个例子,用于展开嵌套的序列[5]:
from collections.abc 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)
而 yield from 用于建立双向通道的用法则可以参考 Fluent Python 里例子[6],这里就不详细地解释这段代码:
# BEGIN YIELD_FROM_AVERAGER from collections import namedtuple Result = namedtuple('Result', 'count average') # the subgenerator def averager(): total = 0.0 count = 0 average = None while True: term = yield if term is None: break total += term count += 1 average = total/count return Result(count, average) # the delegating generator def grouper(results, key): while True: results[key] = yield from averager() # the client code, a.k.a. the caller def main(data): results = {} for key, values in data.items(): group = grouper(results, key) next(group) for value in values: group.send(value) group.send(None) report(results) # output report def report(results): for key, result in sorted(results.items()): group, unit = key.split(';') print(f'{result.count:2} {group:5} averaging {result.average:.2f}{unit}') data = { 'girls;kg': [40.9, 38.5, 44.3, 42.2, 45.2, 41.7, 44.5, 38.0, 40.6, 44.5], 'girls;m': [1.6, 1.51, 1.4, 1.3, 1.41, 1.39, 1.33, 1.46, 1.45, 1.43], 'boys;kg': [39.0, 40.8, 43.2, 40.8, 43.1, 38.6, 41.4, 40.6, 36.3], 'boys;m': [1.38, 1.5, 1.32, 1.25, 1.37, 1.48, 1.25, 1.49, 1.46], } if __name__ == '__main__': main(data)
可能对于熟练掌握 Python 的程序员来说,yield 和 yield from 相关的语法充满了美感。但对于刚入门的我来说,除了生成器语法让我感觉到了美感,其他的语法都让我理解起来很是费解。不过还好,asyncio 库融入了 Python 的标准库里,关键字 async 和 await 的引入,将会让我们更少地在编写协程时去使用 yield 和 yield from。 但不管怎么样,yield 都是 Python 里非常特别的一个关键字,值得花时间好好掌握了解。
以上是Python中關鍵字yield有什麼作用的詳細內容。更多資訊請關注PHP中文網其他相關文章!

Python和C 各有優勢,選擇應基於項目需求。 1)Python適合快速開發和數據處理,因其簡潔語法和動態類型。 2)C 適用於高性能和系統編程,因其靜態類型和手動內存管理。

選擇Python還是C 取決於項目需求:1)如果需要快速開發、數據處理和原型設計,選擇Python;2)如果需要高性能、低延遲和接近硬件的控制,選擇C 。

通過每天投入2小時的Python學習,可以有效提升編程技能。 1.學習新知識:閱讀文檔或觀看教程。 2.實踐:編寫代碼和完成練習。 3.複習:鞏固所學內容。 4.項目實踐:應用所學於實際項目中。這樣的結構化學習計劃能幫助你係統掌握Python並實現職業目標。

在兩小時內高效學習Python的方法包括:1.回顧基礎知識,確保熟悉Python的安裝和基本語法;2.理解Python的核心概念,如變量、列表、函數等;3.通過使用示例掌握基本和高級用法;4.學習常見錯誤與調試技巧;5.應用性能優化與最佳實踐,如使用列表推導式和遵循PEP8風格指南。

Python適合初學者和數據科學,C 適用於系統編程和遊戲開發。 1.Python簡潔易用,適用於數據科學和Web開發。 2.C 提供高性能和控制力,適用於遊戲開發和系統編程。選擇應基於項目需求和個人興趣。

Python更適合數據科學和快速開發,C 更適合高性能和系統編程。 1.Python語法簡潔,易於學習,適用於數據處理和科學計算。 2.C 語法複雜,但性能優越,常用於遊戲開發和系統編程。

每天投入兩小時學習Python是可行的。 1.學習新知識:用一小時學習新概念,如列表和字典。 2.實踐和練習:用一小時進行編程練習,如編寫小程序。通過合理規劃和堅持不懈,你可以在短時間內掌握Python的核心概念。

Python更易學且易用,C 則更強大但複雜。 1.Python語法簡潔,適合初學者,動態類型和自動內存管理使其易用,但可能導致運行時錯誤。 2.C 提供低級控制和高級特性,適合高性能應用,但學習門檻高,需手動管理內存和類型安全。


熱AI工具

Undresser.AI Undress
人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover
用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool
免費脫衣圖片

Clothoff.io
AI脫衣器

Video Face Swap
使用我們完全免費的人工智慧換臉工具,輕鬆在任何影片中換臉!

熱門文章

熱工具

SAP NetWeaver Server Adapter for Eclipse
將Eclipse與SAP NetWeaver應用伺服器整合。

mPDF
mPDF是一個PHP庫,可以從UTF-8編碼的HTML產生PDF檔案。原作者Ian Back編寫mPDF以從他的網站上「即時」輸出PDF文件,並處理不同的語言。與原始腳本如HTML2FPDF相比,它的速度較慢,並且在使用Unicode字體時產生的檔案較大,但支援CSS樣式等,並進行了大量增強。支援幾乎所有語言,包括RTL(阿拉伯語和希伯來語)和CJK(中日韓)。支援嵌套的區塊級元素(如P、DIV),

DVWA
Damn Vulnerable Web App (DVWA) 是一個PHP/MySQL的Web應用程序,非常容易受到攻擊。它的主要目標是成為安全專業人員在合法環境中測試自己的技能和工具的輔助工具,幫助Web開發人員更好地理解保護網路應用程式的過程,並幫助教師/學生在課堂環境中教授/學習Web應用程式安全性。 DVWA的目標是透過簡單直接的介面練習一些最常見的Web漏洞,難度各不相同。請注意,該軟體中

Atom編輯器mac版下載
最受歡迎的的開源編輯器

禪工作室 13.0.1
強大的PHP整合開發環境