为什么这个 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是同一个对象,在同一个内存中。
我是这么想的。
当我运行
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是同一个对象,在同一个内存中。 我是这么想的。