我正在编写一个循环遍历4个列表迭代器,如下所示:
it0 = iter(['foo','bar'])
it1 = iter(['bar','foo'])
it2 = iter(['foo','foo'])
it3 = iter(['bar','bar'])
try:
a, b, c, d = map(next, (it0, it1, it2, it3))
while True:
#some operations that can raise ValueError
if a+b+c+d == 'foobar'*2:
a, b, c, d = map(next, (it0, it1, it2, it3))
elif a+b == 'foobar':
c, d = map(next,(it2, it3))
else:
a, b = map(next,(it0, it1))
except StopIteration:
pass
但是当我在Python3中运行此代码时,我会在预期的ValueError: not enough values to unpack (expected 2, got 0)
之前获得StopIteration
。
我不想在ValueError
语句中捕获except
,因为while循环中的某些操作也可能导致ValueError
停止该程序。
next
如何在分配之前不提出异常?
在Python2.7中,首先引发StopIteration
。
答案 0 :(得分:4)
StopIteration
被引发,但是元组赋值吞噬了它,因为它看到StopIteration
作为来自map()
迭代器的信号 it 已完成生成值:
>>> i0, i1 = iter(['foo']), iter([])
>>> m = map(next, (i0, i1))
>>> next(m)
'foo'
>>> next(m)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
StopIteration
>>> i0, i1 = iter(['foo']), iter([])
>>> m = map(next, (i0, i1))
>>> a, b = m
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ValueError: not enough values to unpack (expected 2, got 1)
这是正常行为;期望迭代器作为输入的Python内置函数总是使用StopIteration
作为'迭代完成'信号,并且元组解包必须在这里迭代。
首先将map()
输出转换为列表并测试长度,分别对每个可迭代使用next()
,而不是使用map()
,或在本地捕获ValueError
。 / p>
测试长度必须重新提升StopIteration
:
values = list(map(next, (it0, it1, it2, it3)))
if len(values) < 4:
raise StopIteration
a, b, c, d = values
请注意,此处list()
已吞下StopIteration
例外。
仅为ValueError
操作抓取map()
:
try:
a, b, c, d = map(next, (it0, it1, it2, it3))
except ValueError:
raise StopIteration
通过单独调用每个迭代器上的map()
来完全不使用next()
:
a, b, c, d = next(it0), next(it1), next(it2), next(it3)
或使用列表理解:
a, b, c, d = [next(i) for i in (it0, it1, it2, it3)]
这两个都确保在分配之前调用next()
,而不是在赋值期间调用。
答案 1 :(得分:0)
根据Martijn Pieters的回答,使用list comprehension直接起作用:
a, b, c, d = [next(it) for it in (it0, it1, it2, it3)]
这个post阐明了为什么for
在迭代上捕获StopIteration
但不在循环体中捕获。{/ p>
另一种可能的方式,利用default
的{{1}}参数:
next