为什么这个 Python 代码(将列表扩展与自身的映射组合在一起)会使我的系统死机?

Why does this Python code (compositing a list extension with a map of itself) make my system freeze up?

当我运行

a = ['a']
a.extend(map(lambda x: 'b' + x, a))

如果我 运行 它作为来自 shell 的 Python 脚本,然后 运行 将其

锁定我的系统,直到我可以执行 Ctrl+C来自口译员让我不得不硬关闭我的笔记本电脑。

然而,

a = ['a']
a.extend(list(map(lambda x: 'b' + x, a)))

工作正常并给出了预期的结果。

为什么会这样?

起初,我认为这可能是因为我试图用 a 本身上 运行 的映射函数扩展 a,所以我写道:

a = ['a']
tmp = map(lambda x: 'b' + x, a)
a.extend(tmp)

然而,那也冻结了。

同样,这似乎工作正常:

a = ['a']
tmp = list(map(lambda x: 'b' + x, a))
a.extend(tmp)

为什么会这样?

我在 Python 3.4.3.

上执行此操作

在python3中,迭代器将从map()函数生成。

当看到a.extend()函数时,python会发现你想用a相关的迭代器扩展列表a,并自动帮助你进行迭代.

迭代从这里开始。

首先,它是 'a' 中的一个。 map() 函数中的迭代器给出 'a''ba' 是从您的 lambda 表达式生成的,并附加到列表 a 中。 a 现在变成 ['a', 'ba']

然后,map() 函数中的迭代器发现迭代 a 没有给出 StopIteration,因为 a 的新朋友 'ba''来了。因此 map() 函数中的迭代器将 'ba' 交给 lambda 进行处理。此处生成 'bba' 并推入 a.

这就是 a 无限传播的原理。

以下代码可能会有所帮助:

a = ['a']
import time
a.extend(map(lambda x: ('b' + x, print(x), time.sleep(1))[0], a))

并且很容易理解为什么您使用 list() 将迭代器转换为静态列表不会触发此操作。

这是因为在 Python 3.x map() 函数中 returns 一个迭代器,它使用传递给它的列表的引用作为第二个参数。因此,当您遍历 map 迭代器时,您也在扩展列表,并且这会无限期地继续下去,因此您要么得到 MemoryError,要么以无限循环结束。

显示此行为的示例 -

>>> m = map(lambda x: a.extend(x), a)
>>> m
<map object at 0x021E0E70>
>>> for i,x in enumerate(m):
...     print("Hello")
...
Hello
Hello
.... # lots of Hello
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 1, in <lambda>
MemoryError

所以当你这样做时 - a.extend(map(lambda x: 'b' + x, a))。您正在做类似于 -

的事情
a = ['a']
for x in a:
    a.extend('b'+x)

如果您尝试上面的代码,您仍然会得到 MemoryError 或无限循环。

当你这样做时 -

a.extend(list(map(lambda x: 'b' + x, a)))

在扩展列表 a 之前,您通过将迭代器转换为列表来用尽迭代器,因此它不会以无限循环结束。在这种情况下,您正在做类似于-

的事情
a = ['a']
templist = []
for x in a:
    templist.extend('b' + x)
a.extend(templist)

所以这就是您没有收到错误的原因。请注意上面的代码可能不是 python 内部运行 map 的方式,它只是类似的东西。

我觉得Python的对象管理机制和C/C++不一样,看这个:

a = ['a'] 

for x in a: a.append('b')

如果你输入你的Python命令行,你会遇到一个死循环,当你输入CTRL+C后,

>>> a

你会得到一个很长的列表,其中包含 'a' 和 'b',我认为在 for 循环中,a.append('b') 的 a 和 a是同一个对象,在同一个内存中。 我是这么想的。