在python 3 map()的映射函数中引发StopIteration处理不正确吗?

时间:2016-05-21 11:34:34

标签: python-3.x dictionary stopiteration

考虑以下示例:

def fn(x):
    if x > 2:
        raise StopIteration
    return x
results = list(map(fn, range(5)))
print(results)

当我使用python 2运行时,我得到了我的预期:

Traceback (most recent call last):
  File "example.py", line 5, in <module>
    results = list(map(fn, range(5)))
  File "example.py", line 3, in fn
    raise StopIteration
StopIteration

但是,如果我使用python 3运行它,程序不会以StopIteration异常结束。它打印出以下结果:

[0, 1, 2]

python 3中的map函数(特别是python 3.5.1)似乎捕获并处理StopIteration异常,就像提供的iterable抛出它一样。这是一个错误吗?

2 个答案:

答案 0 :(得分:1)

正如@thebjorn在评论中所说,错误不是从map()而是从list()引发的,因为Python 3中的map()是懒惰的(这是技术术语):它不是调用函数本身。

相反,它返回一个迭代器,然后由list()消耗。

我们来看这个例子:

>>> def fn(x):
...     if x > 2:
...         raise Exception()
...     return x
... 
>>> results = map(fn, range(5))
>>> results = list(results)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 3, in fn
Exception

如您所见,调用list()时会引发异常,因为list()会读取迭代器,从而导致调用自定义函数。

答案 1 :(得分:1)

我相信很明显为什么会发生这种情况:当fn抛出时,python误解为迭代结束时应该由迭代器抛出,当没有更多元素要迭代时(我相信你知道)此异常在迭代协议中扮演这一特殊角色)。

我猜Python2的map首先将值从try / catch块中的迭代器中拉出来,然后再按fn处理它(这里没有try / catch)。 Python3(懒惰)map可能对try / catch错误没有任何作用,即它隐式地重新抛出异常,无论它是由迭代器还是fn生成。

这是Python3中的错误吗?首先要注意的是,修复它可能会花费map的很多性能,因为迭代器的每次拉动都必须尝试捕获。这就是说,无害的解决方案可能是明确禁止将StopIteration用于其他目的。

这种说法甚至可能存在于一些PEP中:)