令人困惑的python收益行为

问题描述 投票:2回答:2

我今天遇到了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的东西?

python generator yield
2个回答
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)

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)的结果,结果就是这个<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变体基本上意味着“继续调用温度并屈服于它产生的任何东西,直到它说它完成了”。这个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)
© www.soinside.com 2019 - 2024. All rights reserved.