web-dev-qa-db-ja.com

Python 3:ジェネレーターのメソッドを送信する

sendメソッドがわかりません。発電機の操作に使われているとのことですが。ただし、構文は次のとおりです:generator.send(value)

どういうわけか、値が現在のyield式の結果になる必要がある理由を理解できません。私は例を用意しました:

_def gen():
    for i in range(10):
        X = yield i
        if X == 'stop':
            break
        print("Inside the function " + str(X))

m = gen()
print("1 Outside the function " + str(next(m)) + '\n')
print("2 Outside the function " + str(next(m)) + '\n')
print("3 Outside the function " + str(next(m)) + '\n')
print("4 Outside the function " + str(next(m)) + '\n')
print('\n')
print("Outside the function " + str(m.send(None)) + '\n') # Start generator
print("Outside the function " + str(m.send(77)) + '\n')
print("Outside the function " + str(m.send(88)) + '\n')
#print("Outside the function " + str(m.send('stop')) + '\n')
print("Outside the function " + str(m.send(99)) + '\n')
print("Outside the function " + str(m.send(None)) + '\n')
_

結果は次のとおりです。

_1 Outside the function 0

Inside the function None
2 Outside the function 1

Inside the function None
3 Outside the function 2

Inside the function None
4 Outside the function 3



Inside the function None
Outside the function 4

Inside the function 77
Outside the function 5

Inside the function 88
Outside the function 6

Inside the function 99
Outside the function 7

Inside the function None
Outside the function 8
_

まあ、率直に言って、それは私を驚かせています。

  1. ドキュメントでは、yieldステートメントが実行されると、ジェネレーターの状態がフリーズし、_expression_list_の値がnextの呼び出し元に返されることがわかります。まあ、それは起こっていないようです。 gen()内でifステートメントとprint関数を実行できるのはなぜですか。
  2. 関数の内部と外部でXが異なる理由をどのように理解できますか? OK。 send(77)が77をmに送信すると仮定します。さて、yield式は77になります。それでは_X = yield i_とは何ですか?そして、関数内の77が外部で発生した場合、どのように5に変換されますか?
  3. 最初の結果文字列がジェネレーター内で起こっていることを反映していないのはなぜですか?

とにかく、これらのsendおよびyieldステートメントについてどういうわけかコメントできますか?

27
Kifsif

ジェネレーターでsendと式yieldを使用すると、コルーチンとして扱われます。順次インターリーブで実行できるが、呼び出し元と並行して実行できない別の実行スレッド。

呼び出し元がR = m.send(a)を実行すると、オブジェクトaがジェネレーターの入力スロットに配置され、ジェネレーターに制御が移り、応答を待ちます。ジェネレータは、_X = yield i_の結果としてオブジェクトaを受け取り、別のyield式にヒットするまで実行されます。 _Y = yield j_。次に、jを出力スロットに入れ、制御を呼び出し元に戻し、再開されるまで待機します。呼び出し元は、R = m.send(a)の結果としてjを受け取り、別のS = m.send(b)ステートメントにヒットするまで実行します。

R = next(m)R = m.send(None)とまったく同じです。ジェネレーターの入力スロットにNoneを入れているので、ジェネレーターが_X = yield i_の結果をチェックすると、XNoneになります。

比喩として、 ダムウェイター を考えてみましょう:

Dumb waiter

サーバーが顧客から注文を受け取ると、彼らはパッドをダムウェイターに入れ、それをキッチンにsendし、ハッチで皿を待ちます。

_R = kitchen.send("Ham omelette, side salad")
_

シェフ(ハッチで待っていた)が注文を受け取り、料理を準備し、レストランにyieldsして、次の注文を待ちます。

_next_order = yield [HamOmelette(), SideSalad()]
_

サーバー(ハッチで待っていた)が料理を顧客に持って行き、別の注文などで戻ってきます。

サーバーとシェフの両方が注文をsendした後または料理をyieldした後、ハッチで待機するため、一度に何かをするのは1人だけです。つまり、プロセスはシングルスレッドです。発電機の機械(ダムウェイター)がインターリーブの実行を処理するため、両側で通常の制御フローを使用できます。

51
ecatmur

特にジェネレーターでsend()を呼び出す場合、最も混乱する部分はこの行_X = yield i_です。実際にあなたが知る必要がある唯一のことは:

語彙レベル:next()send(None)と等しい

インタプリタレベルで:_X = yield i_以下の行に等しい(ORDER MATTERS):

_yield i
# won't continue until next() or send() is called
# and this is also the entry point of next() or send()
X = the_input_of_send
_

そして、2行のコメントが正確な理由です。ジェネレーターはi(yield i)を返すため、初めてsend(None)を呼び出す必要があります。 before値をXに割り当てます

18
xxmajia
def gen():
    i = 1
    while True:
        i += 1
        x = yield i
        print(x)

m = gen()
next(m)
next(m)
m.send(4)

結果

None
4

上記のより単純化されたコードを見てください。
あなたの混乱につながったのは 'x = yield i'ステートメントだと思います。このステートメントは、iに割り当てられたsend()メソッドから受け入れられた値を言っているのではなく、xに割り当てられています。代わりに、値iはyieldステートメントによってジェネレーターに返され、xはsend()メソッドによって割り当てられます.1つのステートメントは2つのことを同時に実行します。

6
Interaction