Python におけるキーワード yield の機能は何ですか?
Python における yield キーワードの役割: 1. 関数をジェネレーターに変更します。ジェネレーターを使用すると、システム リソースを効果的に節約し、不必要なメモリ使用を回避できます。2. コンテキスト マネージャーの定義に使用されます。3.コルーチン; 4. from と連携して、サブジェネレーターを消費してメッセージを配信するために使用される yield from を形成します。
yield の使用には、次の 4 つの一般的な状況があります。
-
1 つはジェネレーター、
要約すると、ジェネレーター内のコードは yield に達すると戻り、返される内容は yield 後の式です。次回ジェネレーターの内部コードが実行されるとき、最後の状態から継続されます。 yield キーワードを使用すると、関数をジェネレーターに簡単に変更できます。
- # 2 つ目はコンテキスト マネージャーの定義に使用されます。
- 3 つ目はコルーチン
- 4 番目は、from と連携して、yield from を形成することです。これは、サブジェネレーターを消費してメッセージを配信するために使用されます。
>>> 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 になります。インタープリター関数が値を生成するまで実行を続けると、値 None がインタープリターに返され、再び一時停止されます。
- 次に、send(2) メソッドを使用してジェネレーターの呼び出しを続けます。value は受信数値 2 を受け取り、value = (生成値) を実行し続け、数値 2 を通訳、その後一時停止します。
- その後、インタプリタは throw(TypeError, "spam") メソッドを介して再度呼び出します。ジェネレータは実行を再開し、例外をスローします。ジェネレータは例外をキャッチし、例外 TypeError を渡します。 ( 'spam') が変数 value に代入され、次にプログラムが value = (生成値) まで再度実行され、TypeError('spam') がインタプリタに返されます。
- 最後に、プログラムは close() メソッドを呼び出し、ジェネレーター関数の位置で GeneratorExit をスローします。例外がスローされ、ジェネレーターは正常に終了し、最後に対応する最も外側の try を実行します。ステートメントの最終ブランチは Clean up を出力します。
ジェネレーター
予想どおり、yield に初めて遭遇するのは間違いなくジェネレーター関数内です。ジェネレーターは、数値やその他の種類の値を継続的に生成するために使用される関数で、for ループまたは next() 関数を通じて 1 つずつ呼び出すことができます。ここで強調する必要があるのは、ジェネレーターには代入のない yield 式が含まれているため、次の 2 つの形式は同等であるということです [2]:def integers_1(): for i in range(4): yield i + 1def integers_2(): for i in range(4): value = yield i + 1
>>> 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()
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')
Coroutine
コルーチンの概念は美しさに満ちており、人々の作業パターンと非常に一致しています。それを完全にマスターするにはある程度の努力が必要です。しかし、マルチスレッドはコルーチンよりもはるかに多くの問題を引き起こす可能性があるため、努力する価値はあります。以下は、Python クックブック [4] の yield 式のみを使用して記述されたコルーチンの例です。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 中国語 Web サイトの他の関連記事を参照してください。

ホットAIツール

Undresser.AI Undress
リアルなヌード写真を作成する AI 搭載アプリ

AI Clothes Remover
写真から衣服を削除するオンライン AI ツール。

Undress AI Tool
脱衣画像を無料で

Clothoff.io
AI衣類リムーバー

AI Hentai Generator
AIヘンタイを無料で生成します。

人気の記事

ホットツール

メモ帳++7.3.1
使いやすく無料のコードエディター

SublimeText3 中国語版
中国語版、とても使いやすい

ゼンドスタジオ 13.0.1
強力な PHP 統合開発環境

ドリームウィーバー CS6
ビジュアル Web 開発ツール

SublimeText3 Mac版
神レベルのコード編集ソフト(SublimeText3)

ホットトピック









LinuxターミナルでPythonバージョンを表示する際の許可の問題の解決策PythonターミナルでPythonバージョンを表示しようとするとき、Pythonを入力してください...

PythonのPandasライブラリを使用する場合、異なる構造を持つ2つのデータフレーム間で列全体をコピーする方法は一般的な問題です。 2つのデータがあるとします...

10時間以内にコンピューター初心者プログラミングの基本を教える方法は?コンピューター初心者にプログラミングの知識を教えるのに10時間しかない場合、何を教えることを選びますか...

fiddlereveryversings for the-middleの測定値を使用するときに検出されないようにする方法

正規表現は、プログラミングにおけるパターンマッチングとテキスト操作のための強力なツールであり、さまざまなアプリケーションにわたるテキスト処理の効率を高めます。

UvicornはどのようにしてHTTPリクエストを継続的に聞きますか? Uvicornは、ASGIに基づく軽量のWebサーバーです。そのコア機能の1つは、HTTPリクエストを聞いて続行することです...

この記事では、numpy、pandas、matplotlib、scikit-learn、tensorflow、django、flask、and requestsなどの人気のあるPythonライブラリについて説明し、科学的コンピューティング、データ分析、視覚化、機械学習、Web開発、Hの使用について説明します。

Pythonでは、文字列を介してオブジェクトを動的に作成し、そのメソッドを呼び出す方法は?これは一般的なプログラミング要件です。特に構成または実行する必要がある場合は...
