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

如果执行deepcopy:

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

谁能解释一下这些副本之间的区别是什么?它与可变和不可变对象有关吗?如果有,你能解释一下吗?


当前回答

不确定上面是否提到过,但理解.copy()创建对原始对象的引用是非常重要的。如果你改变了复制的对象-你改变了原始对象。 .deepcopy()创建新对象并真正复制原始对象到新对象。改变新的深度复制对象不会影响原始对象。

是的,.deepcopy()递归复制原始对象,而.copy()创建一个引用对象到原始对象的一级数据。

因此.copy()和.deepcopy()之间的复制/引用差异是显著的。

其他回答

对于不可变对象,创建一个副本没有多大意义,因为它们不会改变。对于可变对象赋值,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会创建一个新的变量副本。所以现在对原始变量的更改将不会被反映 复制变量,反之亦然。但是复制(浅复制),不创建嵌套对象的副本,而只是 复制嵌套对象的引用。Deepcopy递归复制所有嵌套对象。

一些演示copy和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.deepcopy(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    

让我们在一个图形示例中看看下面的代码是如何执行的:

import copy

class Foo(object):
    def __init__(self):
        pass


a = [Foo(), Foo()]
shallow = copy.copy(a)
deep = copy.deepcopy(a)

下面的代码显示了底层地址在复制、深度复制和赋值时是如何受到影响的。这类似于Sohaib Farooqi用列表展示的东西,但是用类。

from copy import deepcopy, copy

class A(object):
    """docstring for A"""
    def __init__(self):
        super().__init__()

class B(object):
    """docstring for B"""
    def __init__(self):
        super().__init__()
        self.myA = A()

a = B()
print("a is", a)
print("a.myA is", a.myA)
print("After copy")
b = copy(a)
print("b is", b)
print("b.myA is", b.myA)
b.myA = A()
print("-- after changing value")
print("a is", a)
print("a.myA is", a.myA)
print("b is", b)
print("b.myA is", b.myA)

print("Resetting")
print("*"*40)
a = B()
print("a is", a)
print("a.myA is", a.myA)
print("After deepcopy")
b = deepcopy(a)
print("b is", b)
print("b.myA is", b.myA)
b.myA = A()
print("-- after changing value")
print("a is", a)
print("a.myA is", a.myA)
print("b is", b)
print("b.myA is", b.myA)

print("Resetting")
print("*"*40)
a = B()
print("a is", a)
print("a.myA is", a.myA)
print("After assignment")
b = a
print("b is", b)
print("b.myA is", b.myA)
b.myA = A()
print("-- after changing value")
print("a is", a)
print("a.myA is", a.myA)
print("b is", b)
print("b.myA is", b.myA)

这段代码的输出如下:

a is <__main__.B object at 0x7f1d8ff59760>
a.myA is <__main__.A object at 0x7f1d8fe8f970>
After copy
b is <__main__.B object at 0x7f1d8fe43280>
b.myA is <__main__.A object at 0x7f1d8fe8f970>
-- after changing value
a is <__main__.B object at 0x7f1d8ff59760>
a.myA is <__main__.A object at 0x7f1d8fe8f970>
b is <__main__.B object at 0x7f1d8fe43280>
b.myA is <__main__.A object at 0x7f1d8fe85820>
Resetting
****************************************
a is <__main__.B object at 0x7f1d8fe85370>
a.myA is <__main__.A object at 0x7f1d8fe43310>
After deepcopy
b is <__main__.B object at 0x7f1d8fde3040>
b.myA is <__main__.A object at 0x7f1d8fde30d0>
-- after changing value
a is <__main__.B object at 0x7f1d8fe85370>
a.myA is <__main__.A object at 0x7f1d8fe43310>
b is <__main__.B object at 0x7f1d8fde3040>
b.myA is <__main__.A object at 0x7f1d8fe43280>
Resetting
****************************************
a is <__main__.B object at 0x7f1d8fe432b0>
a.myA is <__main__.A object at 0x7f1d8fe85820>
After assignment
b is <__main__.B object at 0x7f1d8fe432b0>
b.myA is <__main__.A object at 0x7f1d8fe85820>
-- after changing value
a is <__main__.B object at 0x7f1d8fe432b0>
a.myA is <__main__.A object at 0x7f1d8fe85370>
b is <__main__.B object at 0x7f1d8fe432b0>
b.myA is <__main__.A object at 0x7f1d8fe85370>

A, b, c, d, a1, b1, c1和d1是对内存中对象的引用,它们由它们的id唯一标识。

An assignment operation takes a reference to the object in memory and assigns that reference to a new name. c=[1,2,3,4] is an assignment that creates a new list object containing those four integers, and assigns the reference to that object to c. c1=c is an assignment that takes the same reference to the same object and assigns that to c1. Since the list is mutable, anything that happens to that list will be visible regardless of whether you access it through c or c1, because they both reference the same object.

C1 =copy.copy(c)是一个“浅拷贝”,它创建一个新列表,并将对新列表的引用赋值给C1。C仍然指向原始的列表。所以,如果你在c1处修改列表,c所指向的列表不会改变。

复制的概念与整数和字符串等不可变对象无关。由于不能修改这些对象,因此不需要在内存的不同位置有相同值的两个副本。因此,整数和字符串,以及其他一些复制概念不适用的对象,只是简单地重新赋值。这就是为什么使用a和b的例子会得到相同的id。

c1=copy.deepcopy(c) is a "deep copy", but it functions the same as a shallow copy in this example. Deep copies differ from shallow copies in that shallow copies will make a new copy of the object itself, but any references inside that object will not themselves be copied. In your example, your list has only integers inside it (which are immutable), and as previously discussed there is no need to copy those. So the "deep" part of the deep copy does not apply. However, consider this more complex list:

E = [[1,2],[4,5,6],[7,8,9]]]

这是一个包含其他列表的列表(也可以将其描述为一个二维数组)。

If you run a "shallow copy" on e, copying it to e1, you will find that the id of the list changes, but each copy of the list contains references to the same three lists -- the lists with integers inside. That means that if you were to do e[0].append(3), then e would be [[1, 2, 3],[4, 5, 6],[7, 8, 9]]. But e1 would also be [[1, 2, 3],[4, 5, 6],[7, 8, 9]]. On the other hand, if you subsequently did e.append([10, 11, 12]), e would be [[1, 2, 3],[4, 5, 6],[7, 8, 9],[10, 11, 12]]. But e1 would still be [[1, 2, 3],[4, 5, 6],[7, 8, 9]]. That's because the outer lists are separate objects that initially each contain three references to three inner lists. If you modify the inner lists, you can see those changes no matter if you are viewing them through one copy or the other. But if you modify one of the outer lists as above, then e contains three references to the original three lists plus one more reference to a new list. And e1 still only contains the original three references.

“深度复制”不仅会复制外部列表,而且还会进入列表内部并复制内部列表,因此两个结果对象不包含任何相同的引用(就可变对象而言)。如果内部列表中有更多的列表(或其他对象,如字典),它们也会被复制。这是“深层复制”的“深层”部分。

正常的赋值操作只是将新变量指向现有对象。文档解释了浅拷贝和深拷贝的区别:

浅拷贝和深拷贝之间的区别只与 复合对象(包含其他对象的对象,如列表或 类实例): 浅拷贝构造一个新的复合对象,然后(在可能的范围内)将对原始对象的引用插入其中。 类中找到的对象的副本,然后递归地插入到该对象中 原创。

这里有一个小示范:

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