2

我正在做一个简单的练习来理解列表分配的机制。如果我将列表 L1 分配给 L2,然后将元素附加到 L1,则 L1 和 L2 都会更改。但是,如果我将列表 L3 分配给 L2 的子切片,然后将元素附加到 L3,则 L3 中的更改与 L2 断开连接。这是如何实现的?L3 现在实际上指向一个不同的位置,其中包含列表的一个子片段,而 L1 和 L2 指向同一个位置。那正确吗?

>>> L1 = []
>>> L2 =[1,2,3]
>>> L1 = L2
>>> L1.append(4)
>>> L1
[1, 2, 3, 4]
>>> L2
[1, 2, 3, 4]
>>> L3 =L2[:2]
>>> L3
[1, 2]
>>> L3.append(5)
>>> L3
[1, 2, 5]
>>> L2
[1, 2, 3, 4]
4

3 回答 3

3

答案很简单:一个 assignment 分配一个reference。这就是为什么更改L1是可见的L2——它们是同一个对象。

但是,切片会创建范围的(浅)副本。因此对 的更改与L3断开连接L2

实际上,为了创建序列的副本,由于不能使用直接赋值,因此可以使用切片代替:

>>> L4 = L2[:]
>>> L4.append(5)
>>> L2
[1, 2, 3, 4]

…然而,这更常见的是通过构造函数完成,即L4 = list(L2).

于 2013-07-21T23:28:55.167 回答
0

为了补充上面的答案,当您拥有可变对象列表而不是整数或字符串时,副本的性质变得很重要。即使切片时,列表的元素仍然指向相同的对象。

>>> a = { 'a': 0 }
>>> b = { 'b' : 0 }
>>> c = { 'c' : 0 }
>>> l = [ a, b, c ]
>>> m = l[ : 2] 

ml包含对相同事物的引用。

>>> m
[{'a': 0}, {'b': 0}]
>>> m[0]['a'] = 1
>>> m
[{'a': 1}, {'b': 0}]
>>> l
[{'a': 1}, {'b': 0}, {'c': 0}]

但是,ml是不同的东西。

>>> m[0] = {'d': 1}
>>> m
[{'d': 1}, {'b': 0}]
>>> l
[{'a': 1}, {'b': 0}, {'c': 0}] 
于 2013-07-22T01:22:24.587 回答
0

(我在示例中浪费了太多时间,@Konrad Rudolph 击败了我,我基本上和他说的是同样的话)

Python 在后台执行相当于 C 指针分配的操作,L1 将列表的地址保存在内存中,而 L2 只是复制了相同的地址。

您只需使用is运算符即可签入代码。

>>> L1 is L2
True

但是,当使用切片时,必须创建一个新列表,因为它与原始列表不同,但是 python 是一个鬼鬼祟祟的混蛋,它不会复制所有内容,它只是复制对内部对象的引用。

这是一个更详细的示例:

>>> L1 = [1, [2, 3], 4]
>>> L2 = L1
>>> L2
[1, [2, 3], 4]
>>> L1[1] is L2[1]
True

好的,所以L1保存了一个列表,里面有一个列表,你可以用字典、字符串或任何其他 python 对象替换它。请注意,is运算符True为列表中的列表返回。

然后,如果你使用切片技巧:

>>> L3 = L1[:2]
>>> L3
[1, [2, 3]]
>>> L3 is L1
False
>>> L3[1] is L2[1]
True

外部列表已更改,但内部的对象保持不变,这称为浅拷贝。事实上,相同的规则一直适用,如果我们在列表中向列表中添加一个新项目:

>>> L3[1].append(9)
>>> L3
[1, [2, 3, 9]]
>>> L1
[1, [2, 3, 9], 4]

(注意添加的 9) 列表里面的L1L3都改变了。

相反,deepcopy当不需要浅拷贝时,您可以这样做:

>>> from copy import deepcopy
>>> L3 = deepcopy(L1)
>>> L3
[1, [2, 3, 9], 4]
>>> L3 is L1
False
于 2013-07-22T02:25:17.357 回答