令人困惑的python产量行为

时间:2019-02-24 20:24:31

标签: python generator yield

今天我遇到了yield的一个有趣的举动,但我并不太了解。这是我的代码:

def a():
    def b(x):
        print("entering b.")
        yield 0
        if x == 0:
            print("calling b.")
            b(x + 1)
            print("return from b.")
        print("leaving b.")

    for x in b(0):
        yield x

for x in a():
    print(x)

输出:

entering b.
0
calling b.
return from b.
leaving b.

令我感到困惑的是,显式调用b(x + 1)不会调用b(!),Python也不会给出任何错误或异常。

现在,显然上面代码中的错误是b(x + 1)应该真正产生b产生的值-因此它应该读为:

for x in b(x + 1):
  yield x

然后一切正常。

还是,yield这件事我应该注意吗?

2 个答案:

答案 0 :(得分:5)

b(x + 1)被调用,但是直到在调用函数的上下文中产生时才执行。

使用yield from产生对b()的调用所产生的所有值并执行主体:

def a():
    def b(x):
        print("entering b.")
        yield 0
        if x == 0:
            print("calling b.")
            yield from b(x + 1)
            print("return from b.")
        print("leaving b.")

    for x in b(0):
        yield x

for x in a():
    print(x)

答案 1 :(得分:3)

answer you got so far is right(我已对其进行投票),但我发现您仍在与之抗争,所以让我们尝试以下变体:

def a():
    def b(x):
        print("entering b.")
        yield 0
        if x == 0:
            print("calling b.")
            temp = b(x + 1)
            print("calling b resulted in temp =", temp)
            print("return from b.")
        print("leaving b.")

    for x in b(0):
        yield x

for x in a():
    print(x)

现在让我们在Python 3.x中运行它:

entering b.
0
calling b.
calling b resulted in temp = <generator object a.<locals>.b at 0x800ac9518>
return from b.
leaving b.

也就是说,将temp设置为调用b(x + 1)的结果,而 result 就是这个<generator object ...>东西。

然后您必须对生成器对象进行 的操作,因此这里是第三个变体:

def a():
    def b(x):
        print("entering b.")
        yield 0
        if x == 0:
            print("calling b.")
            temp = b(x + 1)
            print("calling b resulted in temp =", temp)
            y = next(temp)
            print("by doing next(temp), I got", y)
            print("return from b.")
        print("leaving b.")

    for x in b(0):
        yield x

for x in a():
    print(x)

运行此操作会产生:

entering b.
0
calling b.
calling b resulted in temp = <generator object a.<locals>.b at 0x800ac9518>
entering b.
by doing next(temp), I got 0
return from b.
leaving b.

另一个答案中的yield from变体基本上意味着“继续调用temp并产生它产生的任何结果,直到它说完成为止”。这个y = next(temp)仅叫temp。

针对读者的锻炼:尝试以下引用的第四个变体。尝试在运行之前预测会看到什么。你看到你的预言了吗?

def a():
    def b(x):
        print("entering b.")
        yield 0
        if x == 0:
            print("calling b.")
            temp = b(x + 1)
            print("calling b resulted in temp =", temp)
            y = next(temp)
            print("by doing next(temp), I got", y)
            try:
                print("about to re-enter temp")
                y = next(temp)
                print("with the second next(temp), I got", y)
            except StopIteration:
                print("with the second next(temp), I got StopIteration")
            print("return from b.")
        else:
            print("b had x =", x)
        print("leaving b.")

    for x in b(0):
        yield x

for x in a():
    print(x)