ホームページ  >  記事  >  バックエンド開発  >  Python での yield キーワードの使用法 (コード例) の紹介

Python での yield キーワードの使用法 (コード例) の紹介

不言
不言転載
2018-12-15 10:09:442486ブラウズ

この記事では、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
上記のコードからわかるように、get_generator 関数が呼び出されるとき、関数内のコードは実行されません。 Iterator オブジェクトの場合、関数内のコードは for ループで反復する場合にのみ実行されます。

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 サイトの他の関連記事を参照してください。

声明:
この記事はsegmentfault.comで複製されています。侵害がある場合は、admin@php.cn までご連絡ください。