この記事では、Python での yield キーワードの使用法 (コード例) を紹介します。一定の参考価値があります。必要な友人は参照できます。お役に立てれば幸いです。
yield は Python のキーワードです。私が初めて Python に触れたときは、このキーワードについて少ししか理解していませんでしたが、使いこなしてみると、このキーワードが非常に便利であることがわかりました。この記事yieldの使い方を整理します。
1 yield を使用してジェネレーターを作成する
Python では、ジェネレーターは反復可能なオブジェクトですが、反復可能なオブジェクトは必ずしもジェネレーターではありません。
たとえば、リストは反復可能なオブジェクトです
>>> a = list(range(3)) >>> for i in a: print(i) 0 1 2 3
しかし、リスト オブジェクトのすべての値はメモリに格納されます。データ量が非常に大きい場合、メモリが十分ではない可能性があります。この場合、たとえば、Python は「()」を使用してジェネレーター オブジェクトを構築できます:
>>> b = (x for x in range(3)) >>> for i in b: print(i) 0 1 2 >>> for i in b: print(i) >>>
ジェネレーターは反復可能で、データはリアルタイムで生成され、すべてがメモリに保存されるわけではありません。注目に値します はい、ジェネレーターは一度しか読み取ることができません。上記の実行結果からわかるように、2 番目の for ループによって出力される結果は空です。
実際のプログラミングでは、関数がシリアル化されたデータを生成する必要がある場合、最も簡単な方法は、すべての結果をリストに入れて返すことです。データ量が大きい場合は、使用を検討する必要があります。ジェネレーター リストを直接返す関数を書き直す (効果的な Python、項目 16)。>>> def get_generator(): for i in range(3): print('gen ', i) yield i >>> c = get_generator() >>> c = get_generator() >>> for i in c: print(i) gen 0 0 gen 1 1 gen 2 2
for ループを使用してジェネレーターによって返された値を取得することに加えて、next および send を使用することもできます
>>> c = get_generator() >>> print(next(c)) gen 0 0 >>> print(next(c)) gen 1 1 >>> print(next(c)) gen 2 2 >>> print(next(c)) Traceback (most recent call last): File "<pyshell#59>", line 1, in <module> print(next(c)) StopIteration
>>> c = get_generator() >>> c.send(None) gen 0 0 >>> c.send(None) gen 1 1 >>> c.send(None) gen 2 2 >>> c.send(None) Traceback (most recent call last): File "<pyshell#66>", line 1, in <module> c.send(None) StopIteration
ジェネレーターの結果が読み取られた後、StopIteration 例外が発生します。 generated
2 コルーチンでの yield の使用
#一般的な使用シナリオは、yield を通じてコルーチンを実装することです。次のプロデューサー/コンシューマー モデルを例として取り上げます。 ### import logging # import contextlib # def foobar(): # logging.debug('Some debug data') # logging.error('Some error data') # logging.debug('More debug data') # @contextlib.contextmanager # def debug_logging(level): # logger = logging.getLogger() # old_level = logger.getEffectiveLevel() # logger.setLevel(level) # try: # yield # finally: # logger.setLevel(old_level) # with debug_logging(logging.DEBUG): # print('inside context') # foobar() # print('outside context') # foobar() def consumer(): r = 'yield' while True: print('[CONSUMER] r is %s...' % r) #当下边语句执行时,先执行yield r,然后consumer暂停,此时赋值运算还未进行 #等到producer调用send()时,send()的参数作为yield r表达式的值赋给等号左边 n = yield r #yield表达式可以接收send()发出的参数 if not n: return # 这里会raise一个StopIteration print('[CONSUMER] Consuming %s...' % n) r = '200 OK' def produce(c): c.send(None) n = 0 while n < 5: n = n + 1 print('[PRODUCER] Producing %s...' % n) r = c.send(n) #调用consumer生成器 print('[PRODUCER] Consumer return: %s' % r) c.send(None) c.close() c = consumer() produce(c)
[CONSUMER] r is yield... [PRODUCER] Producing 1... [CONSUMER] Consuming 1... [CONSUMER] r is 200 OK... [PRODUCER] Consumer return: 200 OK [PRODUCER] Producing 2... [CONSUMER] Consuming 2... [CONSUMER] r is 200 OK... [PRODUCER] Consumer return: 200 OK [PRODUCER] Producing 3... [CONSUMER] Consuming 3... [CONSUMER] r is 200 OK... [PRODUCER] Consumer return: 200 OK [PRODUCER] Producing 4... [CONSUMER] Consuming 4... [CONSUMER] r is 200 OK... [PRODUCER] Consumer return: 200 OK [PRODUCER] Producing 5... [CONSUMER] Consuming 5... [CONSUMER] r is 200 OK... [PRODUCER] Consumer return: 200 OK Traceback (most recent call last): File ".\foobar.py", line 51, in <module> produce(c) File ".\foobar.py", line 47, in produce c.send(None) StopIteration
上の例でわかるように、yield 式と send を使用してデータを交換できます。さらに、次のような contextmanager でのより興味深い使用シナリオがあります。
n = yield r r = c.send(n)
import logging import contextlib def foobar(): logging.debug('Some debug data') logging.error('Some error data') logging.debug('More debug data') @contextlib.contextmanager def debug_logging(level): logger = logging.getLogger() old_level = logger.getEffectiveLevel() logger.setLevel(level) try: yield #这里表示with块中的语句 finally: logger.setLevel(old_level) with debug_logging(logging.DEBUG): print('inside context') foobar() print('outside context') foobar()
上記のコードでは、ログ レベルはコンテキスト マネージャー (contextmanager) を使用して一時的に増加し、yield は with ブロック内のステートメントを表します。 ;
概要
yield 式はジェネレーターを作成できるため、リストを直接返す関数を書き直すにはジェネレーターの使用を検討する必要があります。ジェネレーターは一度だけ読み取ることができるため、for ループを使用してトラバースする場合は特に注意してください。ジェネレーターが読み取り後に読み取りを続けると、StopIteration 例外が発生します。実際のプログラミングでは、この例外を基礎として使用できます。読み込みの終了を判断するため;
yield の一般的な使用シナリオはコルーチンの実装です; send 関数と連携することでデータ交換の効果を得ることができます;
yield は、 with ブロック Statement
内の contextmanager によって変更された関数で表現されます。以上がPython での yield キーワードの使用法 (コード例) の紹介の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。