next()在python中对任何/所有都不好玩

时间:2015-02-02 23:39:04

标签: python python-3.x generator generator-expression

我今天遇到了一个错误,因为我使用next()来提取值,而“找不到”会发出StopIteration

通常会暂停程序,但在next次迭代中调用使用all()的函数,因此all提前终止并返回True。< / p>

这是预期的行为吗?是否有风格指南,以帮助避免这种事情?

简化示例:

def error(): return next(i for i in range(3) if i==10)
error() # fails with StopIteration
all(error() for i in range(2)) # returns True

2 个答案:

答案 0 :(得分:24)

虽然这是Python版本(包括3.6版本)的默认行为,但它被认为是该语言的错误,并且计划在Python 3.7中进行更改,以便引发异常。

正如PEP 479所说:

  

生成器和StopIteration的交互目前有点令人惊讶,并且可以隐藏不明显的错误。意外的异常不应导致轻微改变的行为,但应导致嘈杂且易于调试的回溯。目前,在生成器函数内意外引发的StopIteration将被解释为循环结构驱动生成器的迭代结束。

从Python 3.5开始,可以将默认行为更改为3.7的计划。这段代码:

# gs_exc.py

from __future__ import generator_stop

def error():
    return next(i for i in range(3) if i==10)

all(error() for i in range(2))

...引发了以下异常:

Traceback (most recent call last):
  File "gs_exc.py", line 8, in <genexpr>
    all(error() for i in range(2))
  File "gs_exc.py", line 6, in error
    return next(i for i in range(3) if i==10)
StopIteration

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
  File "gs_exc.py", line 8, in <module>
    all(error() for i in range(2))
RuntimeError: generator raised StopIteration

在Python 3.5和3.6 中没有 __future__导入时,会引发警告。例如:

# gs_warn.py

def error():
    return next(i for i in range(3) if i==10)

all(error() for i in range(2))

$ python3.5 -Wd gs_warn.py 
gs_warn.py:6: PendingDeprecationWarning: generator '<genexpr>' raised StopIteration
  all(error() for i in range(2))

$ python3.6 -Wd gs_warn.py 
gs_warn.py:6: DeprecationWarning: generator '<genexpr>' raised StopIteration
  all(error() for i in range(2))

答案 1 :(得分:9)

问题不在于使用all,而是您将生成器表达式作为all的参数。 StopIteration传播到生成器表达式,它实际上并不知道它的来源,所以它做了常用的事情并结束了迭代。

您可以通过将error函数替换为直接引发错误的内容来查看此内容:

def error2(): raise StopIteration

>>> all(error2() for i in range(2))
True

最后一个难题是知道all对空序列的作用:

>>> all([])
True

如果您要直接使用next,您应该准备好自己抓住StopIteration

编辑:很高兴看到Python开发人员认为这是一个错误,并且正在采取措施在3.7中更改它。