2016-05-03 6 views
4

私は、Python 3に発電機で実験し、このかなり不自然ジェネレータを書いた:のPython:ジェネレーターで送信()の挙動

def send_gen(): 
    print(" send_gen(): will yield 1") 
    x = yield 1 
    print(" send_gen(): sent in '{}'".format(x)) 
    # yield # causes StopIteration when left out 


gen = send_gen() 
print("yielded {}".format(gen.__next__())) 

print("running gen.send()") 
gen.send("a string") 

出力:

send_gen(): will yield 1 
yielded 1 
running gen.send() 
    send_gen(): sent in 'a string' 
Traceback (most recent call last): 
    File "gen_test.py", line 12, in <module> 
    gen.send("a string") 
StopIteration 

のでgen.__next__()がラインに到達しx = yield 1し、私はがNoneに割り当てられると思ったが、gen.send()の次にyieldという文を探すだろうから、x = yield 1は「used」、、次にStopIterationとなります。

代わりに、何が起こっていると思わxは、次のyieldを探すためのpythonを試み、その後その後、印刷された「文字列」、を送信しStopIterationを取得してしまうことです。

だから私はこれを試してください:出力

def send_gen(): 
    x = yield 1 
    print(" send_gen(): sent in '{}'".format(x)) 


gen = send_gen() 
print("yielded : {}".format(gen.send(None))) 

yielded : 1 

しかし、今はエラーはありません。 send()は、xNoneを割り当てた後にの次のyieldのステートメントを検索しようとしていないようです。

動作が若干異なるのはなぜですか?これは私がどのように発電機を始動したかと関係がありますか?

答えて

4

挙動がない異なります。 2番目のセットアップでジェネレータの最初のyield式を超えて前進したことはありません。 StopIterationであり、エラーはありません。です。ジェネレータが終了するたびに発生すると予想される信号は正常な動作です。あなたの2番目の例では、あなたは決して発電機の終わりに達しません。

ジェネレータはyield表現に達するたびに、実行は、それが再開されるまで式は発電機の内部で何かを作り出すことができない、右がを一時停止します。どちらかgen.__next__()またはgen.send()は両方gen.send()、またはNoneによって渡された値を生成するyield式で、その時点から実行を再開します。それが役に立ちそうなら、gen.__next__()gen.send(None)として見ることができます。ここで実現するべきことは、gen.send()yieldの場合、送信値を最初にに、に、次のyieldに続けて返すことです。

だから、あなたの最初の例ジェネレータ与えられ、これが起こる:

  1. gen = send_gen()はジェネレータオブジェクトを作成します。コードは関数の最上部で一時停止され、何も実行されません。

  2. あなたはどちらかgen.__next__()gen.send(None)を呼び出します。発電機が開始され、最初のyield表現されるまで実行されます。

    print(" send_gen(): will yield 1") 
    yield 1 
    

    と実行今ポーズgen.__next__()またはgen.send(None)呼び出しが今、yield 1によって得られた値を1返します。発電機が一時停止しているため、x = ...割り当てはまだ実行できません。それは発電機が再び再開されたときにのみ起こります。

  3. あなたの最初の例ではgen.send("a string")と呼ばれ、はいずれもコールを2番目にしないでください。

    x = <return value of the yield expression> # 'a string' in this case 
    print(" send_gen(): sent in '{}'".format(x)) 
    

    を今機能のでStopIterationが上昇し、終了しますので、最初、例えば、発電機機能について再開されます。あなたの第二の例で発電を再開したことがないので

は、発電機の終わりに達していないと何もStopIteration例外も発生しません。発電機は、関数の先頭から始まるので、あなたがgen.send()で送ら何でも返すために、その時点ではyield表現がないことを

注ので、最初gen.send()値は常にNoneでなければならないか、例外が発生します。それは、それが最初yieldの式で一時停止することがありますので、「プライム」ジェネレータに明示gen.__next__()(あるいは、むしろnext(gen)関数呼び出し)を使用するのが最適です。

+0

ああ、私は参照してください。 'x = ...'の説明は多くの助けになりました。ありがとう! – peonicles

3

ここでの決定的な違いは、あなたが二回、しかし、あなただけたら、あなたの第二の例では発電機を打つあなたの最初の例で発電機を襲ってきたということです。

コルーチンを定義した場合、つまり、引数を送信するジェネレータは、最初のyieldステートメントに進むことで事前に「プライミング」する必要があります。それだけであなたは価値を送ることができます。最初の例では、sendを試行する前にgen.__next__()を呼び出すことで、これを明示的に行っています。

gen.send(None)Noneでの送信は、実際にはgen.__next__()またはnext(gen)の呼び出しと同じです)を行うことで、2番目の例では、しかし、2回目の値を送信しようとしなかったので、その場合はStopIterationはありませんでした。ジェネレータは、あなたがもう一度ヒットするのを待っているyieldステートメントで一時停止しているだけです。そのことが、後でそのプリントをまだ見なかった理由です。注意すべき

もう一つのポイントは、あなた第二の例ではNone以外のものを送っていた場合、エラーがあっただろうということです。

TypeError: can't send non-None value to a just-started generator 

これは私が「呼び水」と話していたものですコルーチン。

関連する問題