浅拷贝,深层拷贝和正常赋值操作的区别究竟是什么?
import copy a=”deepak” b=1,2,3,4 c=[1,2,3,4] d={1:10,2:20,3:30} a1=copy.copy(a) b1=copy.copy(b) c1=copy.copy(c) d1=copy.copy(d) print "immutable - id(a)==id(a1)",id(a)==id(a1) print "immutable - id(b)==id(b1)",id(b)==id(b1) print "mutable - id(c)==id(c1)",id(c)==id(c1) print "mutable - id(d)==id(d1)",id(d)==id(d1)
我得到以下结果 –
immutable - id(a)==id(a1) True immutable - id(b)==id(b1) True mutable - id(c)==id(c1) False mutable - id(d)==id(d1) False
如果我执行深度复制 –
a1=copy.deepcopy(a) b1=copy.deepcopy(b) c1=copy.deepcopy(c) d1=copy.deepcopy(d)
结果是一样的 –
immutable - id(a)==id(a1) True immutable - id(b)==id(b1) True mutable - id(c)==id(c1) False mutable - id(d)==id(d1) False
如果我从事作业作业 –
a1=a b1=b c1=c d1=d
那么结果是 –
immutable - id(a)==id(a1) True immutable - id(b)==id(b1) True mutable - id(c)==id(c1) True mutable - id(d)==id(d1) True
有人可以解释这些副本之间究竟有什么区别吗? 它与可变和不可变对象有关吗? 如果是这样,你能解释给我吗?
正常的赋值操作将简单地将新variables指向现有对象。 文档解释浅层和深层拷贝之间的区别:
浅层复制和深层复制之间的区别仅与复合对象(包含其他对象的对象,如列表或类实例)相关:
浅拷贝构造一个新的复合对象,然后(尽可能)将引用插入到原始对象中。
深层副本构造一个新的复合对象,然后recursion地将副本插入到原始对象中。
这里有一个示例:
import copy a = [1, 2, 3] b = [4, 5, 6] c = [a, b]
使用正常的分配操作来复制:
d = c print id(c) == id(d) # True - d is the same object as c print id(c[0]) == id(d[0]) # True - d[0] is the same object as c[0]
使用浅拷贝:
d = copy.copy(c) print id(c) == id(d) # False - d is now a new object print id(c[0]) == id(d[0]) # True - d[0] is the same object as c[0]
使用深层复制:
d = copy.deepcopy(c) print id(c) == id(d) # False - d is now a new object print id(c[0]) == id(d[0]) # False - d[0] is now a new object
对于不可变的对象,不需要复制,因为数据永远不会改变,所以Python使用相同的数据; ID总是一样的。 对于可变对象,由于它们可能会发生变化,[浅]副本会创build一个新对象。
深度复制与嵌套结构有关。 如果你有列表的列表,那么deepcopy也会copies
嵌套列表,所以它是一个recursion的副本。 只是复制,你有一个新的外部列表,但内部列表是引用。
作业不复制。 它只是设置对旧数据的引用。 所以你需要复制创build一个新的列表具有相同的内容。
a,b,c,d,a1,b1,c1和d1是对存储器中的对象的引用,它们由它们的id唯一标识。
赋值操作需要对内存中的对象进行引用,并将该引用分配给新名称。 c=[1,2,3,4]
是一个赋值,创build一个包含这四个整数的新列表对象,并将该对象的引用赋值给c
。 c1=c
是一个赋值,它对同一个对象采用相同的引用 ,并将其赋值给c1
。 由于列表是可变的,因此无论您是通过c
还是c1
访问它,都会看到该列表发生的任何事情,因为它们都引用同一个对象。
c1=copy.copy(c)
是一个“浅拷贝”,它创build一个新的列表,并将对新列表的引用分配给c1
。 c
仍然指向原来的列表。 所以,如果你修改c1
中的列表, c
引用的列表将不会改变。
复制的概念与像整数和string这样的不可变对象无关。 由于您不能修改这些对象,因此不需要在不同位置的内存中拥有相同值的两个副本。 所以整数和string,以及复制概念不适用的其他一些对象都被重新分配。 这就是为什么你的例子与a
和b
导致相同的id。
c1=copy.deepcopy(c)
是一个“深层拷贝”,但在本例中它的function与浅拷贝相同。 深度副本与浅度副本不同,因为浅度副本将创build对象本身的新副本,但是该对象内的任何引用都不会被复制。 在你的例子中,你的列表里面只有整数(它是不可变的),正如前面讨论的那样,不需要拷贝那些整数。 所以深层“深”部分不适用。 但是,考虑这个更复杂的列表:
e = [[1, 2],[4, 5, 6],[7, 8, 9]]
这是一个包含其他列表的列表(您也可以将其描述为一个二维数组)。
如果你在e
上运行一个“浅拷贝”,把它拷贝到e1
,你会发现列表的id会改变,但是列表的每个拷贝都包含对同样三个列表的引用 – 列表里面有整数。 这意味着如果你要做e[0].append(3)
,那么e
就是[[1, 2, 3],[4, 5, 6],[7, 8, 9]]
。 但是e1
也是[[1, 2, 3],[4, 5, 6],[7, 8, 9]]
。 另一方面,如果你随后做了e.append([10, 11, 12])
, e
将是[[1, 2, 3],[4, 5, 6],[7, 8, 9],[10, 11, 12]]
。 但是e1
仍然是[[1, 2, 3],[4, 5, 6],[7, 8, 9]]
。 这是因为外部列表是独立的对象,最初每个对象包含三个引用三个内部列表。 如果您修改内部列表,则无论您是通过一个副本还是另一个副本查看,都可以看到这些更改。 但是,如果您修改上面的一个外部列表,则e
包含三个对原始三个列表的引用,再加上一个对新列表的引用。 而e1
仍然只包含原来的三个引用。
“深层复制”不仅会复制外部列表,而且还会进入列表内部并复制内部列表,以便两个结果对象不包含任何相同的引用(就可变对象而言) 。 如果内部列表中还有其他列表(或其他对象,例如字典),它们也会被复制。 这是“深层复制”的“深层”部分。
我们来看一个graphics化的例子,下面的代码是如何执行的:
import copy class Foo(object): def __init__(self): pass a = [Foo(), Foo()] shallow = copy.copy(a) deep = copy.deepcopy(a)
对于不可变的对象来说,创build一个副本没有什么意义,因为它们不会改变。 对于可变对象assignment
, copy
和deepcopy
行为有所不同。 让我们用例子来谈谈他们每个人。
赋值操作只是将源的引用分配给目标,例如:
>>> i = [1,2,3] >>> j=i >>> hex(id(i)), hex(id(j)) >>> ('0x10296f908', '0x10296f908') #Both addresses are identical
现在i
和j
技术上是指同一个列表。 i
和j
都有相同的内存地址。 其中任何一个的更新都会反映到另一个。 例如:
>>> i.append(4) >>> j >>> [1,2,3,4] #Destination is updated >>> j.append(5) >>> i >>> [1,2,3,4,5] #Source is updated
另一方面, copy
和deepcopy
创build一个variables的新副本。 所以现在改变原来的variables不会被反映到复制variables,反之亦然。 不过, copy(shallow copy)
不会创build嵌套对象的副本,而只是复制嵌套对象的引用。 Deepcopy以recursion方式复制所有嵌套的对象。
一些例子来certificatecopy
和deepcopy
行为:
使用copy
平面列表示例:
>>> import copy >>> i = [1,2,3] >>> j = copy.copy(i) >>> hex(id(i)), hex(id(j)) >>> ('0x102b9b7c8', '0x102971cc8') #Both addresses are different >>> i.append(4) >>> j >>> [1,2,3] #Updation of original list didn't affected copied variable
使用copy
嵌套列表示例:
>>> import copy >>> i = [1,2,3,[4,5]] >>> j = copy.copy(i) >>> hex(id(i)), hex(id(j)) >>> ('0x102b9b7c8', '0x102971cc8') #Both addresses are still different >>> hex(id(i[3])), hex(id(j[3])) >>> ('0x10296f908', '0x10296f908') #Nested lists have same address >>> i[3].append(6) >>> j >>> [1,2,3,[4,5,6]] #Updation of original nested list updated the copy as well
使用deepcopy
平面列表示例:
>>> import copy >>> i = [1,2,3] >>> j = copy.deepcopy(i) >>> hex(id(i)), hex(id(j)) >>> ('0x102b9b7c8', '0x102971cc8') #Both addresses are different >>> i.append(4) >>> j >>> [1,2,3] #Updation of original list didn't affected copied variable
使用deepcopy
嵌套列表示例:
>>> import copy >>> i = [1,2,3,[4,5]] >>> j = copy.copy(i) >>> hex(id(i)), hex(id(j)) >>> ('0x102b9b7c8', '0x102971cc8') #Both addresses are still different >>> hex(id(i[3])), hex(id(j[3])) >>> ('0x10296f908', '0x102b9b7c8') #Nested lists have different addresses >>> i[3].append(6) >>> j >>> [1,2,3,[4,5]] #Updation of original nested list didn't affected the copied variable