1

考虑以下示例:

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(特别是 python 3.5.1)中的map函数似乎捕获并处理StopIteration异常,就好像提供的 iterable 抛出了它一样。这是一个错误吗?

4

2 回答 2

1

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

相反,它返回一个迭代器,然后由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()读取了迭代器,这会导致调用您的自定义函数。

于 2016-05-21T15:22:56.983 回答
1

我相信很清楚为什么会发生这种情况:当fn抛出时,python 将其误解为应该由迭代器抛出的迭代结束,当没有更多元素可以迭代时(我相信你知道这个异常在迭代协议)。

我猜 Python2map首先将值从 try/catch 块中的迭代器中提取出来,然后才通过fn(这里没有 try/catch)处理它。Python3(懒惰)map可能对尝试/捕获错误没有任何作用,即它隐式地重新抛出异常,无论它是由迭代器产生的,还是fn.

这是 Python3 中的错误吗?首先请注意,修复此问题可能会花费相当多的性能,map因为迭代器的每次拉取都必须尝试捕获。这就是说,无害的修复可能是明确禁止将其StopIteration用于其他目的。

有可能,这样的注释甚至存在于某些 PEP 中:)

于 2016-05-21T20:03:34.193 回答