next()与python中的any/all不一致

amw*_*ter 26 python generator generator-expression python-3.x

我今天跑了一个错误,因为我next()用来提取一个值,而'找不到'会发出一个错误StopIteration.

通常会停止程序,但函数使用next是在all()迭代中调用的,所以all刚刚终止并返回True.

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

简化示例:

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
Run Code Online (Sandbox Code Playgroud)

Zer*_*eus 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))
Run Code Online (Sandbox Code Playgroud)

...引发以下异常:

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
Run Code Online (Sandbox Code Playgroud)

在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))
Run Code Online (Sandbox Code Playgroud)

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

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


Mar*_*som 9

问题不在于使用all,而是你有一个生成器表达式作为参数all.该StopIteration被传播到发电机的表达,这并不知道它起源,它确实是这样的平常的事情,结束迭代.

你可以通过error直接引发错误的函数替换你的函数来看到这一点:

def error2(): raise StopIteration

>>> all(error2() for i in range(2))
True
Run Code Online (Sandbox Code Playgroud)

最后一块拼图是知道all空序列的作用:

>>> all([])
True
Run Code Online (Sandbox Code Playgroud)

如果你打算next直接使用,你应该准备抓住StopIteration自己.

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

  • @PadraicCunningham这是相关的:http://stackoverflow.com/a/16814336/1219006 (2认同)