切片操作是否提供深度或浅度副本?
official Python docs表示使用切片运算符并在Python中进行赋值可以创建切片列表的浅表副本。切片操作是否提供深度或浅度副本?
但是,当我写的示例代码:
o = [1, 2, 4, 5]
p = o[:]
当我写:
id(o)
id(p)
我得到不同的ID,并附加一个一个列表中的其他列表中不反映。是不是创建了一个深层副本,还是在那里我错了?
您正在创建一个浅副本,因为嵌套值不会被复制,只会被引用。 A 深副本也会创建列表引用的值的副本。
演示:
>>> 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
这里嵌套字典不被复制;它只是被两个列表引用。新元素42
未共享。
请记住,Python中的所有内容都是一个对象,名称和列表元素仅仅是对这些对象的引用。列表副本创建一个新的外部列表,但新列表仅仅接收对完全相同的对象的引用。
一个适当的深层副本创建和包含在列表中的所有对象,递归的每一个新副本:
>>> from copy import deepcopy
>>> lst_deepcopy = deepcopy(lst)
>>> id(lst_deepcopy[0]) == id(lst[0])
False
你应该知道,使用is
或id
测试可能会产生误导的真实副本是否正在取得与不可变的和包含不可变的字符串,整数和元组等对象。
考虑一个容易理解的例子实习字符串:
>>> l1=['one']
>>> l2=['one']
>>> l1 is l2
False
>>> l1[0] is l2[0]
True
现在做的l1
浅拷贝和测试不可改变的字符串:
>>> l3=l1[:]
>>> l3 is l1
False
>>> l3[0] is l1[0]
True
现在让通过l1[0]
包含字符串的副本:
>>> s1=l1[0][:]
>>> s1
'one'
>>> s1 is l1[0] is l2[0] is l3[0]
True # they are all the same object
T RY一个deepcopy的,每一个元素应该被复制:
>>> from copy import deepcopy
>>> l4=deepcopy(l1)
>>> l4[0] is l1[0]
True
在每种情况下,字符串'one'
被拘留到Python的内部不可改变的字符串和is
的缓存将表明他们是相同的(它们具有相同的id
) 。它的实现和版本取决于实现的内容以及实现的时间,所以你不能依赖它。它可以是一个重要的记忆和性能增强。
您可以强制不立刻被拘禁的例子:
>>> s2=''.join(c for c in 'one')
>>> s2==l1[0]
True
>>> s2 is l1[0]
False
然后你就可以使用Python intern function,使该字符串来表示缓存的对象,如果发现:
>>> l1[0] is s2
False
>>> s2=intern(s2)
>>> l1[0] is s2
True
同样适用于不可变元组:
>>> t1=('one','two')
>>> t2=t1[:]
>>> t1 is t2
True
>>> t3=deepcopy(t1)
>>> t3 is t2 is t1
True
immutables(如整数)的d可变列表可以让列表成员埋葬:
>>> li1=[1,2,3]
>>> li2=deepcopy(li1)
>>> li2 == li1
True
>>> li2 is li1
False
>>> li1[0] is li2[0]
True
所以,你可以使用你知道将复制的东西,但最终的结果是另一个引用的实习不可变对象蟒蛇行动。如果项目是可变的,is
测试只是一个副本的决定性测试。
在Python 3上,'intern'已经被移动到'sys'模块,所以你需要执行'import sys; S = sys.intern(S)' – dawg
那么为什么我得到不同的ID – user2528042
@ user2528042:因为*外部列表对象*是不一样的。 –
@ user2528042因为原始列表*被复制到一个新的对象。只是内部的所有元素都不会被复制,所以如果列表包含可变对象(整数不可变),更改该对象将在原始列表和复制列表中更改它,因为它们都具有对同一对象的引用副本。 – poke