切片操作是否给我一个深或浅的副本?

use*_*042 13 python copy list deep-copy

Python官方文档说,使用切片运算符和Python中的分配,使切片列表的浅表副本.

但是当我编写代码时,例如:

o = [1, 2, 4, 5]
p = o[:]
Run Code Online (Sandbox Code Playgroud)

当我写:

id(o)
id(p)
Run Code Online (Sandbox Code Playgroud)

我得到不同的id并且还附加一个列表并不反映在另一个列表中.它不是在创建一个深层副本,还是在某个地方我出错了?

Mar*_*ers 21

您正在创建浅表副本,因为嵌套值不会被复制,只会被引用.一个拷贝会建立一个列表中引用过的值的副本.

演示:

>>> lst = [{}]
>>> lst_copy = lst[:]
>>> lst_copy[0]['foo'] = 'bar'
>>> lst_copy.append(42)
>>> lst
[{'foo': 'bar'}]
>>> id(lst) == id(lst_copy)
False
>>> id(lst[0]) == id(lst_copy[0])
True
Run Code Online (Sandbox Code Playgroud)

这里嵌套字典不会被复制; 它只是由两个列表引用.新元素42不共享.

请记住,Python中的所有内容都是对象,名称和列表元素仅仅是对这些对象的引用.列表的副本创建新的外部列表,但新列表仅接收对完全相同的对象的引用.

正确的深层复制以递归方式创建列表中包含的每个对象的新副本:

>>> from copy import deepcopy
>>> lst_deepcopy = deepcopy(lst)
>>> id(lst_deepcopy[0]) == id(lst[0])
False
Run Code Online (Sandbox Code Playgroud)

  • @ user2528042:它是浅拷贝并不意味着'id(o)== id(p)`,它意味着`[id(x)for x in o] == [id(x)for x in p]`.看到不同? (3认同)
  • @user2528042:因为*外部列表对象* 不一样。 (2认同)

daw*_*awg 6

您应该知道测试使用isid可能误导是否使用包含不可变项的字符串,整数和元组等不可变和内部对象来创建真正的副本.

考虑一个易于理解的实习字符串示例:

>>> l1=['one']
>>> l2=['one']
>>> l1 is l2
False
>>> l1[0] is l2[0]
True
Run Code Online (Sandbox Code Playgroud)

现在制作一个浅表副本l1并测试不可变字符串:

>>> l3=l1[:]
>>> l3 is l1
False
>>> l3[0] is l1[0]
True
Run Code Online (Sandbox Code Playgroud)

现在复制包含的字符串l1[0]:

>>> s1=l1[0][:]
>>> s1
'one'
>>> s1 is l1[0] is l2[0] is l3[0]
True               # they are all the same object
Run Code Online (Sandbox Code Playgroud)

尝试深色复制,其中应复制每个元素:

>>> from copy import deepcopy
>>> l4=deepcopy(l1)
>>> l4[0] is l1[0]
True
Run Code Online (Sandbox Code Playgroud)

在每种情况下,字符串'one'都被嵌入Python的不可变字符串的内部缓存中,并is显示它们是相同的(它们具有相同的id).它的实现和版本取决于被实现的内容以及何时实现,因此您无法依赖它.它可以是一个重要的记忆和性能增强.

您可以强制一个不会立即实施的示例:

>>> s2=''.join(c for c in 'one')
>>> s2==l1[0]
True
>>> s2 is l1[0]
False
Run Code Online (Sandbox Code Playgroud)

然后你可以使用Python intern函数使该字符串在找到时引用缓存对象:

>>> l1[0] is s2
False
>>> s2=intern(s2)
>>> l1[0] is s2
True
Run Code Online (Sandbox Code Playgroud)

同样适用于不可变元组:

>>> t1=('one','two')
>>> t2=t1[:]
>>> t1 is t2
True
>>> t3=deepcopy(t1)
>>> t3 is t2 is t1
True
Run Code Online (Sandbox Code Playgroud)

可变列表的不可变量(如整数)可以使列表成员被内插:

>>> li1=[1,2,3]
>>> li2=deepcopy(li1)
>>> li2 == li1
True
>>> li2 is li1
False
>>> li1[0] is li2[0]
True
Run Code Online (Sandbox Code Playgroud)

因此,您可以使用您知道将复制某些内容的python操作,但最终结果是对实习的不可变对象的另一个引用.该is测试只是对项目可变的副本进行的决定性测试.