简体   繁体   English

python生成器的收益/回报

[英]yield/return from python generators

Bumped into a behaviour I'm struggling to grasp without an assistance. 陷入一种我在没有帮助的情况下难以掌握的行为。 Here's a recursive function: 这是一个递归函数:

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 raises StopIteration which is not handled by caller so exception is raised before anything returns. return value将引发StopIteration ,调用者无法处理该StopIteration ,因此在返回任何内容之前会引发异常。

If return value is substituted by yield value; return 如果将return value替换为yield value; return yield value; return like this: 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 yield value; raise StopIteration yield value; raise StopIteration or yield value; raise StopIteration(value) yield value; raise StopIterationyield value; raise StopIteration(value) yield value; raise StopIteration(value) or loop is hidden under else clause then exception is handled by caller the way I expect and function eventually returns '0' . else子句下隐藏yield value; raise StopIteration(value)或循环,然后由调用程序按我期望的方式处理异常,函数最终返回'0' All of these also raise exception: raise StopIteration , both bare and with argument, yield; return value 所有这些还引发异常: raise StopIteration ,裸露并带有参数yield; return value yield; return value , and bare return . yield; return value ,并没有return

In short, caller breaks when StopIteration is raised and yield never returned. 简而言之,调用方在StopIteration引发时中断,并且yield从不返回。

Why? 为什么?

PEP380 states that first StopIteration is handled in a different way then others. PEP380指出,首先以不同于其他方式的方式处理StopIteration PEP479 says: 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. 当前,生成器函数内部意外引发的StopIteration将被驱动生成器的循环构造解释为迭代结束。

Apparently, except the first time. 显然,除了第一次。 Still, the details of underlying implementation and exact reasoning behind it are unclear to me. 我仍然不清楚底层实现的细节及其背后的确切原因。

Two more additional questions: 另外两个问题:

  • what's the right way of writing this snippet? 编写此代码段的正确方法是什么? return (yield value) ? 回报(收益率)

  • is it a quirk, a feature, or something else? 是怪癖,功能还是其他?

Edit: fixed mistakes in code 编辑:修复代码中的错误

Edit2: As far as I understand the execution flow in the first snippet is following: Edit2:据我了解,第一个代码段中的执行流程如下:

  1. countdown creates generator from recursive('5', ['1', '2', '3']) countdownrecursive('5', ['1', '2', '3'])创建生成器recursive('5', ['1', '2', '3'])
  2. Which spawns generators all the way down the tree to recursive('11', []) 生成器一直沿树生成recursive('11', [])
  3. At this point StopIteration('11') is raised 此时,将引发StopIteration('11')

And here is the tricky part. 这是棘手的部分。 What happens here? 这里会发生什么? How StopIteration is handled? 如何处理StopIteration

Second snippet: 第二段:

  1. same 相同
  2. same 相同
  3. '11' is yielded upwards until it reaches countdown '11'向上产生直到达到倒数
  4. Where it gets rejected by if '11' == '0' if '11' == '0'被拒绝
  5. Control flow reaches back to yield '11' and raises StopIteration 控制流返回到yield '11'并提高StopIteration
  6. Repeat until '0' 重复直到'0'

From what I see now that is pretty much expected behaviour. 从我现在所看到的,这几乎是预期的行为。 StopIteration is intrecepted by it's caller and does not propagate upward. StopIteration被其调用者感知,并且不会向上传播。 Caller in turn raises StopIteration without arguments. 调用者依次引发不带参数的StopIteration That is why the '11' part of an original exception never reached countdown . 这就是为什么原始异常的'11'部分从未达到countdown Exception in the first snippet traceback was bare StopIteration raised in countdown by recursive('5', ['1', '2', '3'] . 第一个代码段回溯中的异常是recursive('5', ['1', '2', '3']countdown引发的StopIteration

StopIteration is raised when you finally run out of operands. 当您最终用完操作数时,将引发StopIteration Until then, you continue to recur on your list, evaluating results and shortening the list. 在此之前,您将继续出现在列表中,评估结果并缩短列表。 I think that the yield has returned, but it returned to its caller, which was the previous invocation of recursive , rather than countdown . 我认为yield已返回,但返回给调用方,这是先前recursive ,而不是countdown调用。

In the second example, you yield a value, and the ensuing call to recursive is what raises StopIteration , as it immediately hits a return. 在第二个示例中,您产生一个值,随后对recursive的调用引起StopIteration ,因为它立即达到了返回值。

As for the example return (yield 42) , yes, this is a quirk. 至于示例return (yield 42) ,是的,这是一个怪癖。 The poster of that question was stumbling through making a generator, and discovered that code he later thought was wrong, had actually returned something. 这个问题的发布者在制造发电机时绊脚石,发现他后来认为是错误的代码实际上返回了一些东西。

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

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