5

私はジェネレーターとジェネレーター式をいじっていますが、それらがどのように機能するかを完全には理解していません(いくつかの参考資料):

>>> a = (x for x in range(10))
>>> next(a)
0
>>> next(a)
1
>>> a.send(-1)
2
>>> next(a)
3

generator.sendしたがって、無視されたように見えます。yield送信された情報をキャッチする明示的な式がないため、これは理にかなっています(私は推測します) ...

でも、

>>> a = ((yield x) for x in range(10))
>>> next(a)
0
>>> print next(a)
None
>>> print next(a)
1
>>> print next(a)
None
>>> a.send(-1)  #this send is ignored, Why? ... there's a yield to catch it...
2
>>> print next(a)
None
>>> print next(a)
3
>>> a.send(-1)  #this send isn't ignored
-1

私はこれがかなり遠いことを理解しています、そして私は(現在)これのユースケースを考えることができません(それで尋ねないでください;)

私は主に、これらのさまざまなジェネレーターメソッドがどのように機能するか(およびジェネレーター式が一般的にどのように機能するか)を理解しようとしています。なぜ私の2番目の例は賢明な値を生み出すこととを交互に行うのNoneですか?また、私generator.sendの一方が無視され、もう一方が無視されなかった理由を誰かが説明できますか?

4

5 に答える 5

3

ここでの混乱は、ジェネレータ式が隠しを実行していることyieldです。ここでは関数形式です:

def foo():
    for x in range(10):
        yield (yield x)

を実行する.send()と、内部yield xが実行され、が生成されますx。次に、式はの値に評価され.send、次のyieldはそれを生成します。ここでは、より明確な形式になっています。

def foo():
    for x in range(10):
        sent_value = (yield x)
        yield sent_value

したがって、出力は非常に予測可能です。

>>> a = foo()
#start it off
>>> a.next() 
0
#execution has now paused at "sent_value = ?"
#now we fill in the "?". whatever we send here will be immediately yielded.
>>> a.send("yieldnow") 
'yieldnow'
#execution is now paused at the 'yield sent_value' expression
#as this is not assigned to anything, whatever is sent now will be lost
>>> a.send("this is lost") 
1
#now we're back where we were at the 'yieldnow' point of the code
>>> a.send("yieldnow") 
'yieldnow'
#etc, the loop continues
>>> a.send("this is lost")
2
>>> a.send("yieldnow")
'yieldnow'
>>> a.send("this is lost")
3
>>> a.send("yieldnow")
'yieldnow'

編集:使用例。私がこれまでに見た中で最もクールなものは、ツイストのinlineCallbacks機能です。それを説明する記事については、ここを参照してください。その要点は、スレッドで実行される関数を生成できることです。関数が完了すると、twistedは関数の結果をコードに送り返します。したがって、スレッドに大きく依存するコードを非常に直線的で直感的な方法で作成できます。あちこちにたくさんの小さな関数を作成する必要はありません。

潜在的なユースケースで作業する理由の詳細については、 PEP 342を参照してください.send(私が提供したねじれた例は、この変更が提供する非同期I / Oへの恩恵の例です)。

于 2012-09-07T19:24:20.413 に答える
3

実際には2つのソースから生成しているため、少し混乱しています。ジェネレータ式(... for x in range(10))は1つのジェネレータですが、を使用して別のソースを作成しますyieldlist(a)あなたはあなたが得るならそれを見ることができます[0, None, 1, None, 2, None, 3, None, 4, None, 5, None, 6, None, 7, None, 8, None, 9, None]

あなたのコードはこれと同等です:

>>> def gen():
...     for x in range(10):
...         yield (yield x)

ジェネレーターでは、内部イールド(「イールドx」)のみが「使用」されます。これは、外部イールドの値として使用されます。したがって、このジェネレーターは、範囲の値を生成することと、それらの生成に「送信」されるものを生成することの間を行ったり来たりします。内側のyieldに何かを送信すると、それが返されますが、偶数の反復で送信した場合、送信は外側のyieldに送信され、無視されます。

于 2012-09-07T19:25:57.320 に答える
2

このジェネレータは次のように変換されます。

for i in xrange(10):
    x = (yield i)
    yield x

send()/ next()への2回目の呼び出しの結果は無視されます。これは、いずれかのyieldの結果に対して何も行わないためです。

于 2012-09-07T19:24:41.693 に答える
0

あなたが書いたジェネレータは、より冗長なものと同等です。

def testing():
    for x in range(10):
            x = (yield x)
            yield x

ここでわかるようyieldに、ジェネレータ式に暗黙的に含まれている2番目のは、渡した値を保存しないため、ジェネレータの実行がブロックされている場所によっては、機能する場合と機能しsendない場合があります。

于 2012-09-07T19:38:17.937 に答える
-1

実際、このsendメソッドは、明示的に記述したコルーチンの結果であるジェネレーターオブジェクトを操作することを目的としています。ジェネレータ式で意味を理解するのは困難ですが、機能します。

-編集- 私は以前にこれを書きましたが、ジェネレータ式内のyieldは実装全体で予測可能であるため、正しくありません-どのPEPにも記載されていませんが。

ジェネレータ式はyieldキーワードを持つことを意図していません-この場合の動作が定義されているかどうかはわかりません。私たちは少し考えて、あなたの表現で何が起こっているのかを理解し、それらの「なし」がどこから来ているのかを知ることができます。ただし、Pythonでyieldが実装される方法の副作用として(おそらく実装に依存することさえあります)、そうあるべきものとしてではないと仮定します。

簡略化された方法でのジェネレータ式の正しい形式は次のとおりです。

(<expr> for <variable> in <sequence> [if <expr>])

したがって、-<expr> の値ごとに評価されます。使用するべきではないため、不要なだけではありません。<sequence:yield

yieldとメソッドはどちらも、次のsendような完全なコルーチンで使用することを目的としています。

def doubler():
   value = 0
   while value < 100:
       value = 2 * (yield value)

そして、あなたはそれを次のように使うことができます:

>>> a = doubler()
>>> # Next have to be called once, so the code will run up to the first "yield"
... 
>>> a.next()
0
>>> a.send(10)
20
>>> a.send(20)
40
>>> a.send(23)
46
>>> a.send(51)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
StopIteration
>>> 
于 2012-09-07T19:33:16.520 に答える