在使用 map(next, iterables) 的 StopIteration 之前引发 ValueError
ValueError raised before StopIteration with map(next, iterables)
我正在像这样在 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 中 运行 这段代码时,我在预期的 StopIteration
之前得到了 ValueError: not enough values to unpack (expected 2, got 0)
。
我不想在 except
语句中捕获 ValueError
因为 while 循环中的某些操作也可能导致 ValueError
应该停止程序。
为什么 next
没有在赋值前抛出异常?
在Python2.7中,StopIteration
先提出。
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
作为 'iteration is done' 信号,并且元组解包必须在此处迭代。
首先将 map()
输出转换为列表并测试长度,在每个可迭代对象上分别使用 next()
而不是使用 map()
,或者在本地捕获 ValueError
.
测试长度将不得不重新提高StopIteration
:
values = list(map(next, (it0, it1, it2, it3)))
if len(values) < 4:
raise StopIteration
a, b, c, d = values
注意这里list()
吞掉了StopIteration
异常
为 map()
操作捕获 ValueError
:
try:
a, b, c, d = map(next, (it0, it1, it2, it3))
except ValueError:
raise StopIteration
通过在每个迭代器上单独调用 next()
完全不使用 map()
:
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()
在赋值发生之前被调用,而不是在赋值过程中被调用。
根据 Martijn Pieters 的回答,直接使用列表理解是可行的:
a, b, c, d = [next(it) for it in (it0, it1, it2, it3)]
这个 post 阐明了为什么 for
在可迭代对象上捕获 StopIteration
而不是在循环体中。
另一种可能的方式,利用next
的default
参数:
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), (None,)*4)
while all(_ is not None for _ in (a,b,c,d)):
#some operations that can raise ValueError
if a+b+c+d == 'foobar'*2:
a, b, c, d = map(next, (it0, it1, it2, it3), (None,)*4)
elif a+b == 'foobar':
c, d = map(next,(it2, it3), (None,)*2)
else:
a, b = map(next,(it0, it1), (None,)*2)
except StopIteration:
pass
我正在像这样在 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 中 运行 这段代码时,我在预期的 StopIteration
之前得到了 ValueError: not enough values to unpack (expected 2, got 0)
。
我不想在 except
语句中捕获 ValueError
因为 while 循环中的某些操作也可能导致 ValueError
应该停止程序。
为什么 next
没有在赋值前抛出异常?
在Python2.7中,StopIteration
先提出。
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
作为 'iteration is done' 信号,并且元组解包必须在此处迭代。
首先将 map()
输出转换为列表并测试长度,在每个可迭代对象上分别使用 next()
而不是使用 map()
,或者在本地捕获 ValueError
.
测试长度将不得不重新提高StopIteration
:
values = list(map(next, (it0, it1, it2, it3)))
if len(values) < 4:
raise StopIteration
a, b, c, d = values
注意这里list()
吞掉了StopIteration
异常
为 map()
操作捕获 ValueError
:
try:
a, b, c, d = map(next, (it0, it1, it2, it3))
except ValueError:
raise StopIteration
通过在每个迭代器上单独调用 next()
完全不使用 map()
:
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()
在赋值发生之前被调用,而不是在赋值过程中被调用。
根据 Martijn Pieters 的回答,直接使用列表理解是可行的:
a, b, c, d = [next(it) for it in (it0, it1, it2, it3)]
这个 post 阐明了为什么 for
在可迭代对象上捕获 StopIteration
而不是在循环体中。
另一种可能的方式,利用next
的default
参数:
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), (None,)*4)
while all(_ is not None for _ in (a,b,c,d)):
#some operations that can raise ValueError
if a+b+c+d == 'foobar'*2:
a, b, c, d = map(next, (it0, it1, it2, it3), (None,)*4)
elif a+b == 'foobar':
c, d = map(next,(it2, it3), (None,)*2)
else:
a, b = map(next,(it0, it1), (None,)*2)
except StopIteration:
pass