割り当てとyield演算子を含むこのコードはどのように機能しますか?結果はかなり混乱しています。
def test1(x):
for i in x:
_ = yield i
yield _
def test2(x):
for i in x:
_ = yield i
r1 = test1([1,2,3])
r2 = test2([1,2,3])
print list(r1)
print list(r2)
出力:
[1, None, 2, None, 3, None]
[1, 2, 3]
割り当て構文(「yieldexpression」)を使用すると、ジェネレーターを基本的なコルーチンとして扱うことができます。
PEP 342 で最初に提案され、ここに文書化されています: https://docs.python.org/2/reference/expressions.html#yield-expressions
ジェネレーターで動作しているクライアントコードは、send()
メソッドを使用してデータをジェネレーターに戻すことができます。そのデータには、割り当て構文を介してアクセスできます。
send()
も繰り返されるため、実際にはnext()
呼び出しが含まれます。
あなたの例を使用すると、これはコルーチン機能を使用するのがどのようなものになるかです:
_>>> def test1(x):
... for i in x:
... _ = yield i
... yield _
...
>>> l = [1,2,3]
>>> gen_instance = test1(l)
>>> #First send has to be a None
>>> print gen_instance.send(None)
1
>>> print gen_instance.send("A")
A
>>> print gen_instance.send("B")
2
>>> print gen_instance.send("C")
C
>>> print gen_instance.send("D")
3
>>> print gen_instance.send("E")
E
>>> print gen_instance.send("F")
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
StopIteration
_
送信されたデータをキャプチャしない各ループ反復の2番目のyield
のために、送信の一部が失われることに注意してください。
EDIT:例で生成されたNone
sの説明を忘れました。
から https://docs.python.org/2/reference/expressions.html#generator.next :
Next()メソッドを使用してジェネレーター関数を再開すると、現在のyield式は常にNoneと評価されます。
next()
は、反復構文を使用するときに使用されます。
__ = yield i
yield _
_
まず、yield
sは、i
によって参照される値です。例: _1
_。次に、yield
操作によって返される値であるNone
を生成します。これは、ループの各反復で実行されます。
_for i in x:
_ = yield i
_
これは単にyield
si
によって参照される値です。例: _1
_、次にループの次の反復に進み、_2
_、次に_3
_を生成します。
return
とは異なり、yield
キーワードは式で使用できます。
_x = return 0 # SyntaxError
x = yield 0 # perfectly fine
_
これで、インタプリタがyield
を検出すると、示された値が生成されます。ただし、そうすると、その操作は値None
を返します。これは、mylist.append(0)
またはprint('hello')
が値return
をNone
するのと同じです。その結果を__
_のような参照に割り当てると、そのNone
が保存されます。
したがって、最初のスニペットでは、オブジェクトを生成し、次にそのyield
操作の「結果」(None
)を保存し、次にyield
そのNone
を保存します。 2番目のスニペットでは、オブジェクトを生成し、そのyield
操作の「結果」を保存しますが、その結果をyield
しないので、None
は出力には表示されません。
yield
が常にNone
を返すとは限らないことに注意してください。これは、send()
を使用してジェネレーターに送信したものです。この場合は何もなかったので、None
を取得します。 send()
の詳細については、 この回答 を参照してください。
TigerhawkT3の答えを拡張するために、yield操作がコードでNone
を返す理由は、list(r1)
が何も送信していないためですintoジェネレーター。これを試して:
def test1(x):
for i in x:
_ = yield i
yield _
r1 = test1([1, 2, 3])
for x in r1:
print(' x', x)
print('send', r1.send('hello!'))
出力:
x 1
send hello!
x 2
send hello!
x 3
send hello!
これは、ジェネレーターに値を送信すると便利な、やや製造された例です。
def changeable_count(start=0):
current = start
while True:
changed_current = yield current
if changed_current:
current = changed_current
else:
current += 1
counter = changeable_count(10)
for x in range(20):
print(next(counter), end=' ')
print()
print()
print('Sending 51, printing return value:', counter.send(51))
print()
for x in range(20):
print(next(counter), end=' ')
print()
print()
print('Sending 42, NOT printing return value')
print()
counter.send(42)
for x in range(20):
print(next(counter), end=' ')
print()
出力:
10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
Sending 51, printing return value: 51
52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71
Sending 42, NOT printing return value
43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62