StopIteration之前使用map(next,iterables)

时间:2016-04-27 14:39:44

标签: python python-3.x iterator map-function

我正在编写一个循环遍历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

2 个答案:

答案 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