【问题标题】:Why does list(next(iter(())) for _ in range(1)) == []?为什么 list(next(iter(())) for _ in range(1)) == []?
【发布时间】:2017-01-05 23:58:29
【问题描述】:

为什么list(next(iter(())) for _ in range(1)) 返回一个空列表而不是提升StopIteration

>>> next(iter(()))
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
StopIteration
>>> [next(iter(())) for _ in range(1)]
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
StopIteration
>>> list(next(iter(())) for _ in range(1))  # ?!
[]

显式引发StopIteration的自定义函数也会发生同样的事情:

>>> def x():
...     raise StopIteration
... 
>>> x()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 2, in x
StopIteration
>>> [x() for _ in range(1)]
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 2, in x
StopIteration
>>> list(x() for _ in range(1))  # ?!
[]

【问题讨论】:

  • 哇,我真的很喜欢原来“重复”问题中的答案!
  • 这是一个错误,正在使用PEP 479 解决,其中StopIteration 将转换为RuntimeError,这样列表就不会像您期望的那样错误地停止迭代.
  • @TadhgMcDonald-Jensen 请给出答案,以便我投票。
  • 为了后代,这里是提到的"duplicate" questionwheaties。

标签: python for-loop iterator


【解决方案1】:

假设一切顺利,生成器理解 x() for _ in range(1) 应该在完成对 range(1) 的迭代时提升 StopIteration 以指示没有更多项目要打包到列表中。

但是,因为 x() 引发 StopIteration 它最终会提前退出,这意味着此行为是 python 中的一个错误,正在通过 PEP 479 解决

在 python 3.6 中或在 python 3.5 中使用 from __future__ import generator_stop 时,当 StopIteration 传播得更远时,它会转换为 RuntimeError,因此 list 不会将其注册为理解的结尾。当这生效时,错误如下所示:

Traceback (most recent call last):
  File "/Users/Tadhg/Documents/codes/test.py", line 6, in <genexpr>
    stuff = list(x() for _ in range(1))
  File "/Users/Tadhg/Documents/codes/test.py", line 4, in x
    raise StopIteration
StopIteration

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

Traceback (most recent call last):
  File "/Users/Tadhg/Documents/codes/test.py", line 6, in <module>
    stuff = list(x() for _ in range(1))
RuntimeError: generator raised StopIteration

【讨论】:

    【解决方案2】:

    StopIteration 异常用于告诉list 函数的底层机制何时真正停止对已传递给它的可迭代对象进行迭代。在您的情况下,您是在告诉 Python 已传递给 list() 的东西是一个生成器。因此,当生成器在生成任何项目之前抛出StopIteration 时,它会输出一个空列表,因为没有积累任何内容。

    【讨论】:

    • 错误,来自for _ in range(1) 的停止迭代应该引发StopIteration 以完成列表但它没有,而是x() 引发了一个错误,该错误被静默抑制 导致它意外停止。这在 python3.6 中进行了更改,可以在 python 3.5 中与from __future__ import generator_stop 一起使用。见PEP 479
    • @TadhgMcDonald-Jensen 我很高兴看到它得到修复,但仅仅因为它是一个错误,并不能改变这正是正在发生的事实。
    • @TadhgMcDonald-Jensen 我不会说小麦的回答是错误的。但是,如果您可以将其发布为答案,我很乐意接受。
    • 回想起来,我觉得我对此有点苛刻。您说得对,绝对可以预期会抛出 StopIteration 的生成器。但是让StopIteration 从某个地方传播,而不是期望以一种静默退出的方式提前退出任何迭代器拦截它的行为应该被扼杀。 (我想我对那部分仍然感觉有点强烈:P)
    猜你喜欢
    • 1970-01-01
    • 2021-12-21
    • 2021-05-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-05-19
    • 2022-07-21
    • 2021-05-31
    相关资源
    最近更新 更多