简体   繁体   English

令人困惑的python收益行为

[英]Confusing python yield behavior

I came across a funny behavior of yield today that I don't really understand. 我今天遇到了一个有趣的yield行为,我真的不明白。 Here's my code: 这是我的代码:

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)

That outputs: 那输出:

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

What quite confuses me is that explicitly calling b(x + 1) does not call b (!), neither does Python give any error or exception. 令我困惑的是,显式调用b(x + 1)不会调用b (!),Python也不会给出任何错误或异常。

Now, obviously the error in the code above is that b(x + 1) should really yield the value that b yields - so it should read something like: 现在,显然上面代码中的错误是b(x + 1)应该真正产生b产生的值 - 所以它应该读取如下内容:

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

Things work then. 事情就好了。

Still, is this something with yield I should be aware of? 不过,这应该是我应该注意的yield吗?

The b(x + 1) is called, but not executed until yielded in the context of the calling function. 调用b(x + 1) ,但直到在调用函数的上下文中产生才执行。

Using yield from to yield all the values produced by that call to b() and execute the body: 使用yield from产生该调用b()产生的所有值并执行body:

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)

The answer you got so far is right (and I've upvoted it), but I see you're still fighting with this a bit, so let's try this variant: 你到目前为止得到答案是正确的 (我已经对它进行了投票),但是我看到你还在为此而斗争,所以让我们试试这个变种:

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)

Now let's run this in Python 3.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.

That is, temp is set to the result of calling b(x + 1) , and that result is this <generator object ...> thing. 也就是说, temp被设置为调用b(x + 1) 结果结果就是这个<generator object ...>东西。

You then have to do something with the generator object, so here is yet a third variant: 然后,你必须做发电机对象的东西,所以这里又是第三方案:

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)

Running this produces: 运行它会产生:

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.

The yield from variant in the other answer basically means "keep calling temp and yielding whatever it yields, until it says it's done". 另一个答案中变体的yield from基本上意味着“继续调用温度并收益它产生的任何东西,直到它说它完成了”。 This y = next(temp) called temp just once. 这个y = next(temp)调用一次temp。

Exercise for the reader: Try the fourth variant quoted below. 为读者练习:尝试下面引用的第四个变体。 Try to predict, before you run it, what you'll see. 在运行它之前,尝试预测一下你会看到的内容。 Do you see what you predicted? 你看到你的预测吗?

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)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM