考虑以下示例:
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抛出它一样。这是一个错误吗?
答案 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中:)