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

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


当前回答

下面的代码演示了赋值、使用复制方法的浅复制、使用(slice)[:]的浅复制和深度复制之间的区别。下面的示例使用嵌套列表,使差异更加明显。

from copy import deepcopy

########"List assignment (does not create a copy) ############
l1 = [1,2,3, [4,5,6], [7,8,9]]
l1_assigned = l1

print(l1)
print(l1_assigned)

print(id(l1), id(l1_assigned))
print(id(l1[3]), id(l1_assigned[3]))
print(id(l1[3][0]), id(l1_assigned[3][0]))

l1[3][0] = 100
l1.pop(4)
l1.remove(1)


print(l1)
print(l1_assigned)
print("###################################")

########"List copy using copy method (shallow copy)############

l2 = [1,2,3, [4,5,6], [7,8,9]]
l2_copy = l2.copy()

print(l2)
print(l2_copy)

print(id(l2), id(l2_copy))
print(id(l2[3]), id(l2_copy[3]))
print(id(l2[3][0]), id(l2_copy[3][0]))
l2[3][0] = 100
l2.pop(4)
l2.remove(1)


print(l2)
print(l2_copy)

print("###################################")

########"List copy using slice (shallow copy)############

l3 = [1,2,3, [4,5,6], [7,8,9]]
l3_slice = l3[:]

print(l3)
print(l3_slice)

print(id(l3), id(l3_slice))
print(id(l3[3]), id(l3_slice[3]))
print(id(l3[3][0]), id(l3_slice[3][0]))

l3[3][0] = 100
l3.pop(4)
l3.remove(1)


print(l3)
print(l3_slice)

print("###################################")

########"List copy using deepcopy ############

l4 = [1,2,3, [4,5,6], [7,8,9]]
l4_deep = deepcopy(l4)

print(l4)
print(l4_deep)

print(id(l4), id(l4_deep))
print(id(l4[3]), id(l4_deep[3]))
print(id(l4[3][0]), id(l4_deep[3][0]))

l4[3][0] = 100
l4.pop(4)
l4.remove(1)

print(l4)
print(l4_deep)
print("##########################")
print(l4[2], id(l4[2]))
print(l4_deep[3], id(l4_deep[3]))

print(l4[2][0], id(l4[2][0]))
print(l4_deep[3][0], id(l4_deep[3][0]))

其他回答

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

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

import copy

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


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

下面的代码演示了赋值、使用复制方法的浅复制、使用(slice)[:]的浅复制和深度复制之间的区别。下面的示例使用嵌套列表,使差异更加明显。

from copy import deepcopy

########"List assignment (does not create a copy) ############
l1 = [1,2,3, [4,5,6], [7,8,9]]
l1_assigned = l1

print(l1)
print(l1_assigned)

print(id(l1), id(l1_assigned))
print(id(l1[3]), id(l1_assigned[3]))
print(id(l1[3][0]), id(l1_assigned[3][0]))

l1[3][0] = 100
l1.pop(4)
l1.remove(1)


print(l1)
print(l1_assigned)
print("###################################")

########"List copy using copy method (shallow copy)############

l2 = [1,2,3, [4,5,6], [7,8,9]]
l2_copy = l2.copy()

print(l2)
print(l2_copy)

print(id(l2), id(l2_copy))
print(id(l2[3]), id(l2_copy[3]))
print(id(l2[3][0]), id(l2_copy[3][0]))
l2[3][0] = 100
l2.pop(4)
l2.remove(1)


print(l2)
print(l2_copy)

print("###################################")

########"List copy using slice (shallow copy)############

l3 = [1,2,3, [4,5,6], [7,8,9]]
l3_slice = l3[:]

print(l3)
print(l3_slice)

print(id(l3), id(l3_slice))
print(id(l3[3]), id(l3_slice[3]))
print(id(l3[3][0]), id(l3_slice[3][0]))

l3[3][0] = 100
l3.pop(4)
l3.remove(1)


print(l3)
print(l3_slice)

print("###################################")

########"List copy using deepcopy ############

l4 = [1,2,3, [4,5,6], [7,8,9]]
l4_deep = deepcopy(l4)

print(l4)
print(l4_deep)

print(id(l4), id(l4_deep))
print(id(l4[3]), id(l4_deep[3]))
print(id(l4[3][0]), id(l4_deep[3][0]))

l4[3][0] = 100
l4.pop(4)
l4.remove(1)

print(l4)
print(l4_deep)
print("##########################")
print(l4[2], id(l4[2]))
print(l4_deep[3], id(l4_deep[3]))

print(l4[2][0], id(l4[2][0]))
print(l4_deep[3][0], id(l4_deep[3][0]))

下面的代码显示了底层地址在复制、深度复制和赋值时是如何受到影响的。这类似于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>