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

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


当前回答

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

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

这里有一个小示范:

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

其他回答

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

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

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

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

import copy

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


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

深度复制与嵌套结构有关。如果你有列表的列表,那么deepcopy也复制嵌套的列表,所以它是递归复制。通过复制,您有一个新的外部列表,但内部列表是引用。赋值不复制。 为前

import copy
spam = [[0, 1, 2, 3], 4, 5]
cheese = copy.copy(spam)
cheese.append(3)
cheese[0].append(3)
print(spam)
print(cheese)

输出

[[0,1,2,3,3], 4,5] [[0,1,2,3,3], 4,5,3] 复制方法将外部列表的内容复制到新列表,但两个列表的内部列表仍然相同,因此如果你在任何列表的内部列表中做出更改,它将影响两个列表。

但是如果你使用深度复制,它也会为内部列表创建新的实例。

import copy
spam = [[0, 1, 2, 3], 4, 5]
cheese = copy.deepcopy(spam)
cheese.append(3)
cheese[0].append(3)
print(spam)
print(cheese)

输出

[0, 1, 2, 3] [[0, 1, 2, 3, 3], 4, 5, 3]

The GIST to take is this: Dealing with shallow lists (no sub_lists, just single elements) using "normal assignment" rises a "side effect" when you create a shallow list and then you create a copy of this list using "normal assignment". This "side effect" is when you change any element of the copy list created, because it will automatically change the same elements of the original list. That is when copy comes in handy, as it won't change the original list elements when changing the copy elements.

On the other hand, copy does have a "side effect" as well, when you have a list that has lists in it (sub_lists), and deepcopy solves it. For instance if you create a big list that has nested lists in it (sub_lists), and you create a copy of this big list (the original list). The "side effect" would arise when you modify the sub_lists of the copy list which would automatically modify the sub_lists of the big list. Sometimes (in some projects) you want to keep the big list (your original list) as it is without modification, and all you want is to make a copy of its elements (sub_lists). For that, your solution is to use deepcopy which will take care of this "side effect" and makes a copy without modifying the original content.

复制和深度复制操作的不同行为只涉及复合对象(即:包含其他对象(如列表)的对象)。

下面是这个简单的代码示例中说明的差异:

第一个

让我们通过创建一个原始列表和这个列表的副本来检查copy (shallow)的行为:

import copy
original_list = [1, 2, 3, 4, 5, ['a', 'b']]
copy_list = copy.copy(original_list)

现在,让我们运行一些打印测试,看看原始列表与它的复制列表相比表现如何:

Original_list和copy_list的地址不同

print(hex(id(original_list)), hex(id(copy_list))) # 0x1fb3030 0x1fb3328

original_list和copy_list中的元素有相同的地址

print(hex(id(original_list[1])), hex(id(copy_list[1]))) # 0x537ed440 0x537ed440

original_list和copy_list的Sub_elements有相同的地址

print(hex(id(original_list[5])), hex(id(copy_list[5]))) # 0x1faef08 0x1faef08

修改original_list元素不会修改copy_list元素

original_list.append(6)
print("original_list is:", original_list) # original_list is: [1, 2, 3, 4, 5, ['a', 'b'], 6]
print("copy_list is:", copy_list) # copy_list is: [1, 2, 3, 4, 5, ['a', 'b']]

修改copy_list元素不会修改original_list元素

copy_list.append(7)
print("original_list is:", original_list) # original_list is: [1, 2, 3, 4, 5, ['a', 'b'], 6]
print("copy_list is:", copy_list) # copy_list is: [1, 2, 3, 4, 5, ['a', 'b'], 7]

修改original_list sub_elements会自动修改copy_list sub_elements

original_list[5].append('c')
print("original_list is:", original_list) # original_list is: [1, 2, 3, 4, 5, ['a', 'b', 'c'], 6]
print("copy_list is:", copy_list) # copy_list is: [1, 2, 3, 4, 5, ['a', 'b', 'c'], 7]

修改copy_list sub_elements会自动修改original_list sub_elements

copy_list[5].append('d')
print("original_list is:", original_list) # original_list is: [1, 2, 3, 4, 5, ['a', 'b', 'c', 'd'], 6]
print("copy_list is:", copy_list) # copy_list is: [1, 2, 3, 4, 5, ['a', 'b', 'c', 'd'], 7]

第二个

让我们来检查deepcopy的行为,通过做和copy相同的事情(创建一个原始列表和这个列表的副本):

import copy
original_list = [1, 2, 3, 4, 5, ['a', 'b']]
copy_list = copy.copy(original_list)

现在,让我们运行一些打印测试,看看原始列表与它的复制列表相比表现如何:

import copy
original_list = [1, 2, 3, 4, 5, ['a', 'b']]
copy_list = copy.deepcopy(original_list)

Original_list和copy_list的地址不同

print(hex(id(original_list)), hex(id(copy_list))) # 0x1fb3030 0x1fb3328

original_list和copy_list中的元素有相同的地址

print(hex(id(original_list[1])), hex(id(copy_list[1]))) # 0x537ed440 0x537ed440

original_list和copy_list的Sub_elements有不同的地址

print(hex(id(original_list[5])), hex(id(copy_list[5]))) # 0x24eef08 0x24f3300

修改original_list元素不会修改copy_list元素

original_list.append(6)
print("original_list is:", original_list) # original_list is: [1, 2, 3, 4, 5, ['a', 'b'], 6]
print("copy_list is:", copy_list) # copy_list is: [1, 2, 3, 4, 5, ['a', 'b']]

修改copy_list元素不会修改original_list元素

copy_list.append(7)
print("original_list is:", original_list) # original_list is: [1, 2, 3, 4, 5, ['a', 'b'], 6]
print("copy_list is:", copy_list) # copy_list is: [1, 2, 3, 4, 5, ['a', 'b'], 7]

修改original_list sub_elements不会修改copy_list sub_elements

original_list[5].append('c')
print("original_list is:", original_list) # original_list is: [1, 2, 3, 4, 5, ['a', 'b', 'c'], 6]
print("copy_list is:", copy_list) # copy_list is: [1, 2, 3, 4, 5, ['a', 'b'], 7]

修改copy_list sub_elements不会修改original_list sub_elements

copy_list[5].append('d')
print("original_list is:", original_list) # original_list is: [1, 2, 3, 4, 5, ['a', 'b', 'c', 'd'], 6]
print("copy_list is:", copy_list) # copy_list is: [1, 2, 3, 4, 5, ['a', 'b', 'd'], 7]

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.

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