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處理Zipf定律這一統計概念,並展示Python在處理該定律時讀取和排序大型文本文件的效率。 您可能想知道Zipf分佈這個術語是什麼意思。要理解這個術語,我們首先需要定義Zipf定律。別擔心,我會盡量簡化說明。 Zipf定律 Zipf定律簡單來說就是:在一個大型自然語言語料庫中,最頻繁出現的詞的出現頻率大約是第二頻繁詞的兩倍,是第三頻繁詞的三倍,是第四頻繁詞的四倍,以此類推。 讓我們來看一個例子。如果您查看美國英語的Brown語料庫,您會注意到最頻繁出現的詞是“th

本文解釋瞭如何使用美麗的湯庫來解析html。 它詳細介紹了常見方法,例如find(),find_all(),select()和get_text(),以用於數據提取,處理不同的HTML結構和錯誤以及替代方案(SEL)

處理嘈雜的圖像是一個常見的問題,尤其是手機或低分辨率攝像頭照片。 本教程使用OpenCV探索Python中的圖像過濾技術來解決此問題。 圖像過濾:功能強大的工具圖像過濾器

PDF 文件因其跨平台兼容性而廣受歡迎,內容和佈局在不同操作系統、閱讀設備和軟件上保持一致。然而,與 Python 處理純文本文件不同,PDF 文件是二進製文件,結構更複雜,包含字體、顏色和圖像等元素。 幸運的是,借助 Python 的外部模塊,處理 PDF 文件並非難事。本文將使用 PyPDF2 模塊演示如何打開 PDF 文件、打印頁面和提取文本。關於 PDF 文件的創建和編輯,請參考我的另一篇教程。 準備工作 核心在於使用外部模塊 PyPDF2。首先,使用 pip 安裝它: pip 是 P

本教程演示瞭如何利用Redis緩存以提高Python應用程序的性能,特別是在Django框架內。 我們將介紹REDIS安裝,Django配置和性能比較,以突出顯示BENE

本文比較了Tensorflow和Pytorch的深度學習。 它詳細介紹了所涉及的步驟:數據準備,模型構建,培訓,評估和部署。 框架之間的關鍵差異,特別是關於計算刻度的

Python是數據科學和處理的最愛,為高性能計算提供了豐富的生態系統。但是,Python中的並行編程提出了獨特的挑戰。本教程探討了這些挑戰,重點是全球解釋

本教程演示了在Python 3中創建自定義管道數據結構,利用類和操作員超載以增強功能。 管道的靈活性在於它能夠將一系列函數應用於數據集的能力,GE


熱AI工具

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

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

Undress AI Tool
免費脫衣圖片

Clothoff.io
AI脫衣器

AI Hentai Generator
免費產生 AI 無盡。

熱門文章

熱工具

WebStorm Mac版
好用的JavaScript開發工具

MinGW - Minimalist GNU for Windows
這個專案正在遷移到osdn.net/projects/mingw的過程中,你可以繼續在那裡關注我們。 MinGW:GNU編譯器集合(GCC)的本機Windows移植版本,可自由分發的導入函式庫和用於建置本機Windows應用程式的頭檔;包括對MSVC執行時間的擴展,以支援C99功能。 MinGW的所有軟體都可以在64位元Windows平台上運作。

Dreamweaver CS6
視覺化網頁開發工具

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

SublimeText3 英文版
推薦:為Win版本,支援程式碼提示!