c + = map(lambda n:n * 2,c)杀死Python 3 shell。为什么?

Nit*_*tin 4 python python-2.7 python-3.x

Python 3.6.4 (v3.6.4:d48ecebad5, Dec 18 2017, 21:07:28)
[GCC 4.2.1 (Apple Inc. build 5666) (dot 3)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> c = [1, 2]
>>> c += map(lambda n: n *2, range(1, 3))
>>> c
[1, 2, 2, 4]
>>> c = [1, 2]
>>> c += map(lambda n: n *2, c)
Killed!
Run Code Online (Sandbox Code Playgroud)

但是,同样适用于Python 2,其中map生成的列表不同于Python 3中的生成器。那么为什么c += map(lambda n: n *2, range(1, 3))起作用?

Ant*_*ala 7

c += foo相当于c = c.__iadd__(foo)。如果c为列表,则该list.__iadd__方法接受任何可迭代的函数,包括生成器表达式:

>>> x = []
>>> x += (i for i in range(3))
>>> x
[0, 1, 2]
Run Code Online (Sandbox Code Playgroud)

这即使list.__add__不会支持生成器表达式:

>>> x + (i for i in range(3))
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: can only concatenate list (not "generator") to list
Run Code Online (Sandbox Code Playgroud)

代码在Python 3中崩溃,因为map生成器在正在扩展的同一列表上进行迭代,但是当前位置始终位于尾部后面两个元素。的list.__iadd__,因为它们是从迭代中提取将总是由一个新元素添加到所述端之一; 发生这种情况时,map生成器所保存的列表迭代器使用的索引将增加...但是在列表的末尾添加了一个新元素,因此在当前迭代位置之后始终有2个元素,最终Python用完了内存不足或计算机因交换而磨碎。

即行为与

for elem in c:
    c.append(elem * 2)
Run Code Online (Sandbox Code Playgroud)

(对Wim表示感谢

Python 2版本之所以map能够正常工作,是因为它将调用之前创建一个列表c.__iadd__。2个元素的新列表将传递给c.__iadd__。同样,c += map(lambda n: n * 2, range(1, 3))之所以可行,是因为在Python 2 range(1, 3)中将创建一个新列表,而在Python 3中将创建一个范围序列对象,这两个对象是不同的

如果map用生成器表达式替换,则也可以在Python 2中触发病理行为:

Python 2.7.15rc1 (default, Nov 12 2018, 14:31:15) 
[GCC 7.3.0] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> x = [1, 2]
>>> x += (n * 2 for n in x)
Run Code Online (Sandbox Code Playgroud)