在没有帮助的情况下,我遇到了一种难以理解的行为。这是一个递归函数:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| 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,调用方不处理它,因此在返回任何内容之前引发异常。
如果return value被这样的yield value; return取代:
1 2 3 4 5 6 7 8
| 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) |
或者yield value; raise StopIteration或yield value; raise StopIteration(value)或循环隐藏在else子句下,然后由调用方按我期望的方式处理异常,函数最终返回'0'。所有这些也都提出了例外:raise StopIteration,包括裸的和有争议的,yield; return value和裸的return。
简而言之,当StopIteration被提升而yield从未返回时,调用方中断。
为什么?
PEP380指出,首先处理StopIteration的方式与其他方式不同。PEP47 9表示:
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:据我所知,第一个代码段中的执行流程如下:
countdown用recursive('5', ['1', '2', '3'])创建发电机。
从树下一直到recursive('11', [])都会产生发电机。
此时,StopIteration('11')被提起。
这是最棘手的部分。这里发生了什么?如何处理StopIteration?
第二段:
相同的
相同的
'11'向上产生,直至倒计时。
被if '11' == '0'拒绝
控制流回到yield '11',提升StopIteration。
重复,直到'0'。
从我现在看到的情况来看,这几乎是人们所期待的行为。StopIteration由它的调用者传入,不向上传播。而调用者则不带参数地引发StopIteration。这就是为什么原始例外中的'11'部分从未达到countdown的原因。第一个片段回溯中的例外是StopIteration在countdown中被recursive('5', ['1', '2', '3']引发。
- 你能解释一下你想要这个代码做什么吗?
- @Scott Hunter这是这个问题的一部分np.reddit.com/r/dailybrogrammer/comments/6fe9cv/…,为了可读性,我简化了它。现在我感兴趣的是,为什么直截了当的return value失败了,更复杂的解决方案背后的原因是什么?
- 我相信你的困惑源于不理解发电机是如何工作的。return导致发电机异常终止,引发异常。一般来说,return不应出现在发电机中。
- @删掉我的问题就是关于那个不自然终止的细节。引擎盖下面会发生什么?return value转换成StopIteration(value)好,但是接下来会发生什么?如果yield已经返回,并且没有被截取,为什么StopIteration会被呼叫方截取?谢谢你的建议,我想我会坚持的。
- 当我运行你的代码时,我没有得到跟踪。相反,我在int和str之间得到了一个无效的+操作,因为您返回了一个整数,而您的处理需要一个字符串。最重要的是,我一点也不清楚函数和生成器之间的关系。
- @修剪代码中已修复的错误。
当操作数最终用完时,会引发StopIteration。在此之前,您将继续在您的列表中重复出现,评估结果并缩短列表的长度。我认为收益率已经恢复,但它回到了它的调用方,这是以前调用recursive,而不是countdown。
在第二个示例中,您将生成一个值,随后调用recursive将引发StopIteration,因为它会立即返回。
至于示例return (yield 42),是的,这是一个怪癖。这个问题的海报在制作一个生成器时遇到了障碍,发现他后来认为是错误的代码实际上返回了一些东西。