未执行的 yield 语句块函数到 运行?

Unexecuted yield statement blocks function to run?

在下面的简化代码中,我想重用一个循环来先做一个准备并产生结果。

但是,准备 (bar()) 函数永远不会执行。

yield 语句是否改变了函数的流程?

def bar(*args,**kwargs):
    print("ENTER bar")
    pass

def foo(prepare=False):
    print("ENTER foo")
    for x in range(1,10):
        if prepare:
            bar(x)
        else:
            yield x


foo(prepare=True)

r = foo(prepare=False)
for x in r:
    pass

因为 foo 定义包含一个 yield,它不会 运行 像一个普通函数,即使你像一个函数一样调用它(例如 foo(prepare=True) )。

运行 foo() 带有任何参数都会 return 生成器对象,适合迭代。在您尝试迭代该生成器对象之前,定义的主体不会是 运行。

new coroutine syntax 在定义的 start 处放置了一个关键字,因此本质上的变化不会隐藏在函数体内。

问题是 yield 语句会将函数更改为返回生成器并改变函数的行为。

基本上这意味着在调用生成器的 .next 函数时函数执行到 yield 或函数终止(在这种情况下它引发 StopIteration 异常).

因此,您应该做的是确保即使无法达到 yield 语句也能对其进行迭代。喜欢:

r = foo(prepare=True)
for x in r:
    pass 

在这种情况下,循环将立即终止,因为没有到达 yield 语句。

在我看来,这里的实际解释是:

Python 懒惰地评估 if 条件!

我会解释:

当你打电话给

foo(prepare=True)

就这样,什么也没有发生,尽管你可能认为 bar(x) 会被执行 10 次。但真正发生的是 'no-one' 要求 foo(prepare=True) 调用的 return 值,因此 if 未被评估,但如果您使用 return 则可能来自 foo.

的值

在对 foo 的第二次调用中,迭代 return 值 r,python 必须计算 return 值,它确实如此,我会证明:

案例一

r = foo(prepare=True)
for x in r:
    pass

这里输出'ENTER bar'9次。这意味着 bar 被执行了 9 次。

案例二

r = foo(prepare=False)
for x in r:
    pass

在这种情况下,没有像预期的那样打印“ENTER bar”。


总而言之,我会说:

  • 有些情况下Python执行Lazy Evaluation, one of them is the if statement.

  • 在 Python,

    中并不是所有的东西都是惰性求值的

例如:

# builds a big list and immediately discards it
sum([x*x for x in xrange(2000000)])

对比

# only keeps one value at a time in memory
sum(x*x for x in xrange(2000000))

关于 python 中的懒惰和热切求值,继续阅读 here