为什么这里没有抛出StopIteration?

2 投票
2 回答
568 浏览
提问于 2025-04-29 12:05

看看这个问题,关键点在下面:

>>> scan = iter('FHUR203459')
>>> while True:
        print(next(scan))


F
H
U
R
2
0
3
4
5
9
Traceback (most recent call last):
  File "<pyshell#11>", line 2, in <module>
    print(next(scan))
StopIteration
>>> scan = iter('FHUR203459')
>>> for i in range(12):  # 12 * 2 for each join is 24, much longer than the string; should raise error.
        print(''.join(next(scan) for i in range(2)))


FH
UR
20
34
59







>>>

换句话说,我们可以看到在这两种情况下,迭代器都到了尽头,但只有在第一种情况下会抛出一个 StopIteration 的错误,尽管在两种情况下都在到达尽头后使用了 next()。为什么在 join 中使用它似乎没有出现这个错误?这算不算一个bug呢?

暂无标签

2 个回答

2

str.join() 方法会对生成器使用 list(),而这个调用会忽略掉 StopIteration 这个异常。

任何 消耗 迭代器的操作 都必须 处理 StopIteration;这时候究竟是什么引发了这个异常并不重要,可能是生成器表达式本身,或者是生成器表达式中使用的任何东西:

>>> def raises_stopiteration(): raise StopIteration
... 
>>> next(raises_stopiteration() for _ in range(10))
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 1, in <genexpr>
  File "<stdin>", line 1, in raises_stopiteration
StopIteration
>>> list(raises_stopiteration() for _ in range(10))
[]
2

在第一个例子中,StopIteration 这个情况没有被处理。但是在第二个例子中,

''.join(next(scan) for i in range(2))

我们把一个 生成器表达式 传给了 ''.join,这个方法会处理由 next(scan) 引发的 StopIteration,并且每次都会正常退出。所以 ''.join 产生了空字符串。

你可以稍微修改一下,给 ''.join 传一个列表,然后自己看看会抛出异常,像这样

>>> scan = iter('FHUR203459')
>>> for i in range(12):
...     print(''.join([next(scan) for i in range(2)]))
... 
FH
UR
20
34
59
Traceback (most recent call last):
  File "<input>", line 2, in <module>
  File "<input>", line 2, in <listcomp>
StopIteration

这就表明 StopIteration 确实被抛出了,而这次是 列表推导式 遇到了问题。

撰写回答