yield/return 来自 python 台发电机

yield/return from python generators

遇到了我在没有帮助的情况下难以掌握的行为。这是一个递归函数:

OPERATORS = ['+', '-', '*', '/']

def recursive(value, operands):
    if not operands:
        return value
    for operator in OPERATORS:
        new_value = str(eval(value + operator + operands[-1]))
        new_operands = operands[:-1]
        yield from recursive(new_value, new_operands)

def countdown(value, operands):
    return next(i for i in recursive(value, operands) if i == '0')

ans = countdown('5', ['1', '2', '3'])
print(ans)

return value 引发 StopIteration 调用者未处理,因此在 returns.

之前引发异常

如果 return value 像这样被 yield value; return 代替:

def recursive(value, operands):
    if not operands:
        yield value
        return
    for operator in OPERATORS:
        new_value = str(eval(value + operator + operands[-1]))
        new_operands = operands[:-1]
        yield from recursive(new_value, new_operands)

or yield value; raise StopIteration or yield value; raise StopIteration(value) or loop is hidden under else clause then exception is handled by caller a way I expect and function eventually returns '0'.所有这些也会引发异常:raise StopIteration、裸和带参数、yield; return value 和裸 return.

简而言之,调用者在引发 StopIteration 并且 yield 从未返回时中断。

为什么?

PEP380 states that first StopIteration is handled in a different way then others. PEP479 说:

Currently, StopIteration raised accidentally inside a generator function will be interpreted as the end of the iteration by the loop construct driving the generator.

显然,除了第一次。尽管如此,我仍不清楚底层实现的细节及其背后的确切原因。

另外两个问题:

编辑: 修正了代码中的错误

Edit2: 据我了解第一个片段中的执行流程如下:

  1. countdownrecursive('5', ['1', '2', '3'])
  2. 创建生成器
  3. 它一直沿着树生成发电机 recursive('11', [])
  4. 此时StopIteration('11')提出

这是棘手的部分。这里发生了什么? StopIteration 是如何处理的?

第二个片段:

  1. 一样
  2. 一样
  3. '11'向上产生直到倒计时
  4. 它被 if '11' == '0'
  5. 拒绝的地方
  6. 控制流回到 yield '11' 并引发 StopIteration
  7. 重复直到 '0'

据我现在所见,这几乎是意料之中的行为。 StopIteration 被它的调用者拦截并且不会向上传播。调用者依次引发 StopIteration 而不带参数。这就是为什么原始异常的 '11' 部分从未达到 countdown 的原因。 recursive('5', ['1', '2', '3'].

countdown 中引发了第一个片段回溯中的异常 StopIteration

StopIteration 当你最终 运行 没有操作数时引发。在那之前,您将继续在您的列表中重复出现,评估结果并缩短列表。我认为 yield 已经 returned,但是它 returned 到它的调用者,这是之前调用 recursive,而不是 countdown.

在第二个示例中,您产生了一个值,随后对 recursive 的调用引发了 StopIteration,因为它立即命中了 return.

至于例子return (yield 42),是的,这是一个怪癖。那个问题的发帖者在做一个生成器时跌跌撞撞,发现他后来认为是错误的代码,实际上 return 编了一些东西。