Python:我不知道';我不明白是什么';这台发电机怎么了

Python:我不知道';我不明白是什么';这台发电机怎么了,python,generator,coroutine,Python,Generator,Coroutine,我很好奇这里发生了什么事。熟悉生成器和协同程序的人能很好地解释这段代码吗 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

我很好奇这里发生了什么事。熟悉生成器和协同程序的人能很好地解释这段代码吗

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 I
x=(yield)
,我得到了以下结果

5
None
5
None
5
None
5
None
我怀疑问题可能是因为我试图使用yield语句在同一个函数中同时接收和发送值。这在Python中是不可能的吗

我已经成功地编写了两个使用协同程序的程序,因此我熟悉它们的工作方式,但我对这段代码的行为方式感到困惑。对此有任何见解都将不胜感激

谢谢

编辑:感谢布伦巴恩和联合国大学的回答。当你把问题扩展成这样时,这里发生的事情就更有意义了

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()

我不太明白您的要求,但基本上:当您使用
send
时,它会使生成器中最近达到的产量表达式计算为您发送的值。还要注意的是,
send
将生成器推进到下一个
yield
。有一件事可能会让您感到困惑:您正在生成器内部打印
x
的值,您正在
b
内部打印
next(g)
的值,但是生成器也在
g.send(5)
处生成值,而您没有打印这些值

在第一种情况下,您的第一个
send
会导致
yield i
语句在
b
中计算为5,但在
b
中不使用此值(您不会将
yield i
分配给任何对象),因此它不会执行任何操作。另外,当您执行
send(5)
时,生成器将不产生任何结果(从
x=(yield)
行),但您不打印它,因此您不知道这一点。然后使用
next(g)
再次推进发电机。最近达到的收益率是
x=yield
,但是
next(g)
没有传递任何值,因此
x
被设置为无

在第二种情况下,调用的奇偶性是相反的。现在,您的第一个
send
确实发送到
x=yield
行,因此
x
设置为5。此
send
也会在
b
中生成循环值,但在
a
中忽略此值,不打印它。然后打印
next(g)
,该值为无。在每次后续发送时,
b
打印
x
的值,该值始终为5,因为您总是发送该值,然后
a
打印下一个生成的值,该值始终为零(因为这是
x=yield
生成的值)


我不太明白你所说的“使用yield语句在同一个函数中接收和发送值”是什么意思。您当然可以这样做,但您必须意识到:a)即使在调用
next(g)
时,仍然会发送一个值(None);b)当您使用
traceit
逐行通过程序调用
g.send(5)
时,仍然会生成一个值:

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先打印
None
,然后打印
1
。如果你做到了这一点,我想很清楚为什么你会得到
None
2
,等等——同样的故事又一次发生了,
I
的值不同


另一种情况下,
x=(收益率)
收益率i
是相反的,可以进行类似的分析。

谢谢,正如你所说,我很困惑,因为我没有打印
g.send(5)
的结果。我还忘记了
x=yield
x=yield None
是一样的。