提问人:A.Wan 提问时间:7/17/2013 最后编辑:igauravsehrawatA.Wan 更新时间:9/22/2014 访问量:1766
使用乘法生成子列表 ( * ) 意外行为 [duplicate]
Generating sublists using multiplication ( * ) unexpected behavior [duplicate]
问:
我敢肯定这已经在某个地方得到了答案,但我不确定如何描述它。
假设我想创建一个包含 3 个空列表的列表,如下所示:
lst = [[], [], []]
我以为我这样做很聪明:
lst = [[]] * 3
但是我发现,在调试了一些奇怪的行为之后,这导致将更新附加到一个子列表,例如,更新整个列表,使其而不是 .lst[0].append(3)
[[3], [3], [3]]
[[3], [], []]
但是,如果我用
lst = [[] for i in range(3)]
然后做给出预期的lst[1].append(5)
[[], [5], []]
我的问题是为什么会这样?有趣的是,如果我这样做
lst = [[]]*3
lst[0] = [5]
lst[0].append(3)
然后单元格 0 的“链接”被破坏,我得到 ,但仍然导致 .[[5,3],[],[]]
lst[1].append(0)
[[5,3],[0],[0]
我最好的猜测是,在表单中使用乘法会导致 Python 存储对单个单元格的引用......?[[]]*x
答:
我最好的猜测是,在表单中使用乘法会导致 Python 存储对单个单元格的引用......?
[[]] * x
是的。你可以自己测试一下
>>> lst = [[]] * 3
>>> print [id(x) for x in lst]
[11124864, 11124864, 11124864]
这表明所有三个引用都引用同一个对象。请注意,发生这种情况确实非常有意义1.它只是复制值,在本例中,值是引用。这就是为什么你会看到相同的引用重复了三次。
有趣的是,如果我这样做
lst = [[]]*3
lst[0] = [5]
lst[0].append(3)
然后单元格 0 的“链接”被破坏,我得到 ,但仍然导致 .
[[5,3],[],[]]
lst[1].append(0)
[[5,3],[0],[0]
您更改了占用 ;也就是说,您为 .但是您没有更改其他元素的值,它们仍然引用它们引用的同一对象。并且仍然引用完全相同的实例,因此当然将项目附加到会导致也看到该更改。lst[0]
lst[0]
lst[1]
lst[2]
lst[1]
lst[2]
这是人们在指针和引用方面犯的一个经典错误。这是一个简单的类比。你有一张纸。在上面,你写下某人家的地址。你现在拿起那张纸,复印两次,这样你最终会得到三张写有相同地址的纸。现在,拿起第一张纸,潦草地写出上面写的地址,然后给别人家写一个新地址。另外两张纸上写的地址有变化吗?不。不过,这正是您的代码所做的。这就是为什么其他两个项目不会改变的原因。此外,想象一下,地址仍在第二张纸上的房子的主人在他们的房子上建造了一个附加车库。现在我问你,地址在第三张纸上的房子有附加车库吗?是的,确实如此,因为它与第二张纸上写着地址的房子完全相同。这解释了有关第二个代码示例的所有内容。
1:你没想到Python会调用一个“复制构造函数”,是吗?呕吐。
这是因为序列乘法只是重复引用。当你写入 时,你创建一个包含两个元素的新列表,但这两个元素在内存中是同一个对象,即一个空列表。因此,一个的变化反映在另一个。相比之下,推导会在每次迭代中创建一个新的独立列表:[[]] * 2
>>> l1 = [[]] * 2
>>> l2 = [[] for _ in xrange(2)]
>>> l1[0] is l1[1]
True
>>> l2[0] is l2[1]
False
他们引用的是相同的列表。
从常见问题解答中:
“ * 不创建副本,它只创建对现有 对象。
您猜测使用 [[]] * x 形式的乘法会导致 Python 存储对单个单元格的引用是正确的。
因此,您最终会得到一个包含对同一列表的 3 个引用的列表。
基本上,在第一个示例中发生的情况是,正在创建一个列表,其中包含对同一内部列表的多个引用。下面是一个细分。
>>> a = []
>>> b = [a]
>>> c = b * 3 # c now contains three references to a
>>> d = [ a for _ in xrange(4) ] # and d contains four references to a
>>> print c
[[], [], []]
>>> print d
[[], [], [], []]
>>> a.append(3)
>>> print c
[[3], [3], [3]]
>>> print d
[[3], [3], [3], [3]]
>>> x = [[]] * 3 # shorthand equivalent to c
>>> print x
[[], [], []]
>>> x[0].append(3)
>>> print x
[[3], [3], [3]]
以上等同于您的第一个示例。现在每个列表都有自己的变量,希望更清楚为什么。 的计算结果为 ,因为两个表达式的计算结果都为同一个对象 ()。c[0] is c[1]
True
a
第二个示例创建多个不同的内部列表对象。
>>> c = [[], [], []] # this line creates four different lists
>>> d = [ [] for _ in xrange(3) ] # so does this line
>>> c[0].append(4)
>>> d[0].append(5)
>>> print c
[[4], [], []]
>>> print d
[[5], [], []]
评论