1

ここで何が起こっているのか興味があります。ジェネレーターとコルーチンをよく知っている人がこのコードを説明できますか?

def b():
    for i in range(5):
        yield i
        x = (yield)
        print(x)

def a():
    g = b()
    next(g)
    for i in range(4):
        g.send(5)
        print(next(g))

a()

出力

None
1
None
2
None
3
None
4

しかし、3 行目と 4 行目を切り替えるyield ix = (yield)、次のようになります。

5
None
5
None
5
None
5
None

問題は、yield ステートメントを使用して、同じ関数で値の送受信の両方を試みたことが原因であると思われます。これはPythonでは不可能ですか?

私はコルーチンを使用するいくつかのプログラムの作成に成功したので、それらの動作についてはよく知っていますが、このコード スニペットの動作については混乱しています。これについての洞察をいただければ幸いです。

ありがとう

編集:答えてくれたBrenBarnとunutbuに感謝します。ここで起こっていることは、問題をそのように拡張するとより理にかなっています。

def b():
    for i in range(5):
        yield i
        x = yield None

def a():
    g = b()
    print('* got', g.send(None) )
    for i in range(4):
        print('+ got', g.send(5) )
        print('- got', g.send(None))

a()
4

2 に答える 2

4

を使用traceitして、プログラムを 1 行ずつ実行します。

import sys
import linecache

class SetTrace(object):
    '''
    with SetTrace(monitor):
        ...
    '''
    def __init__(self, func):
        self.func = func
    def __enter__(self):
        sys.settrace(self.func)
        return self
    def passit(self, frame, event, arg):
        return self.passit
    def __exit__(self, ext_type, exc_value, traceback):
        sys.settrace(self.passit)

def traceit(frame, event, arg):
    '''
    http://www.dalkescientific.com/writings/diary/archive/2005/04/20/tracing_python_code.html
    '''
    if event == "line":
        lineno = frame.f_lineno
        filename = frame.f_globals["__file__"]
        if (filename.endswith(".pyc") or
            filename.endswith(".pyo")):
            filename = filename[:-1]
        name = frame.f_globals["__name__"]
        line = linecache.getline(filename, lineno)
        print("%s  # %s:%s" % (line.rstrip(), name, lineno, ))
    return traceit       

def b():
    for i in range(5):
        yield i
        x = (yield)
        print(x)

def a():
    g = b()                    
    next(g)
    for i in range(4):
        g.send(5)
        print(next(g))

with SetTrace(traceit):
    a()        

私達は手に入れました

g = b()  # __main__:44
next(g)  # __main__:45                 # runs b until you get to a yield
for i in range(5):  # __main__:38
    yield i  # __main__:39             # stop before the yield; resume a
    ^
for i in range(4):  # __main__:46
    g.send(5)  # __main__:47           # resume b; (yield i) expression evals to 5 then thrown away
    x = (yield)  # __main__:40         # stop before yield; resume a
         ^
    print(next(g))  # __main__:48      # next(g) called; resume b; print not called yet
    print(x)  # __main__:41            # next(g) causes (yield) to evaluate to None
None
for i in range(5):  # __main__:38
    yield i  # __main__:39             # yield 1; resume a; `print(next(g))` prints 1
1
for i in range(4):  # __main__:46
    g.send(5)  # __main__:47           # resume b; (yield i) expression evals to 5 then thrown away

右側 (上記) のコメントは、Python がNonethen を出力する理由を説明してい1ます。そこまでたどり着いたらNone、なぜ2, などが得られるのかは明らかだと思いますi.

x = (yield)とが逆になっているもう 1 つのシナリオも、yield i同様に分析できます。

于 2012-10-29T03:04:24.943 に答える
4

あなたが求めていることはよくわかりませんが、基本的には、を使用するsendと、ジェネレーターで最後に到達したyield式が、送信した値に評価されます。sendジェネレーターを次の に進めることにも注意してくださいyield。混乱を招く可能性があることの 1 つはx、ジェネレーターの内部の値を出力していて、内部の値を出力しているnext(g)のにb、ジェネレーターも で値を生成してg.send(5)おり、それらを出力していないことです。

最初のケースでは、最初sendyield iステートメントにより 5 内部bで評価されますが、内部でこの値を使用しないb(何にも代入yield iしない) ため、何もしません。また、実行するsend(5)と、ジェネレーターは (行から) None を生成しx = (yield)ますが、それを出力しないため、これはわかりません。次に、 でジェネレーターを再び進めますnext(g)。最近到達した利回りは ですがx = yieldnext(g)値を渡さないため、xNone に設定されます。

2 番目のケースでは、呼び出しのパリティが逆になります。これで、最初の行にsend送信されるため、5 に設定されます。これにより、 のループ値も生成されますが、この値は無視され、出力されません。次に、None を出力します。後続の送信ごとに、の値を出力します。これは、常に送信するものであるため、常に 5 です。次に、の生成された値を出力します。これは、常に None です (それが生成されるため)。x = yieldxsendbanext(g)bxax = yield

「yieldステートメントを使用して、同じ関数で値を送受信する」という意味がよくわかりません。確かにこれを行うことができますが、次のことを認識しておく必要があります。a) を呼び出しても、値 (None) は送信されますnext(g)。b) を呼び出しても値が生成されますg.send(5)

于 2012-10-29T02:45:19.613 に答える