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

我不太明白你在问什么,但基本上:当你使用 时send,它会导致生成器中最近到达的 yield 表达式评估为你发送的值。另请注意,send将生成器推进到下一个yield. 可能让您感到困惑的一件事是,您正在打印x生成器内部的值,并且正在打印next(g)inside的值b,但是生成器也产生了 at 的值g.send(5),而您没有打印这些值。

在您的第一种情况下,您的第一个send导致yield i语句评估为 5 inside b,但您没有在内部使用此值b(您没有分配yield i任何东西),所以它什么也不做。此外,当您这样做时send(5),生成器会产生 None (从该x = (yield)行),但您不打印它,所以您不知道这一点。然后用 再次推进生成器next(g)。最近达到的产量是x = yield,但next(g)不传递任何值,因此x设置为无。

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

我不太明白“使用 yield 语句在同一个函数中接收和发送值”的意思。您当然可以这样做,但您必须意识到: a) 即使您调用 ,仍然会发送一个值 (None) next(g);b) 当您调用g.send(5).

于 2012-10-29T02:45:19.613 回答
4

用于逐行traceit执行程序:

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)和是相反的。yield i

于 2012-10-29T03:04:24.943 回答