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

其他回答

对于不可变对象,不需要复制,因为数据永远不会改变,所以Python使用相同的数据;id总是相同的。对于可变对象,因为它们可能会改变,[浅]复制创建一个新对象。

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

赋值不复制。它只是将引用设置为旧数据。因此,您需要复制来创建一个具有相同内容的新列表。

在python中,当我们将list、tuples、dict等对象赋值给另一个对象时,通常使用' = '符号,python通过引用创建copy ' s。也就是说,我们有一个这样的列表:

list1 = [ [ 'a' , 'b' , 'c' ] , [ 'd' , 'e' , 'f' ]  ]

然后我们给这个列表分配另一个列表,像这样:

list2 = list1

然后,如果我们在python终端中输出list2,我们将得到:

list2 = [ [ 'a', 'b', 'c'] , [ 'd', 'e', ' f ']  ]

list1和list2都指向相同的内存位置,对其中任何一个的任何改变都会导致在两个对象中可见的变化,即两个对象都指向相同的内存位置。 如果我们像这样改变list1:

list1[0][0] = 'x’
list1.append( [ 'g'] )

那么list1和list2都将是:

list1 = [ [ 'x', 'b', 'c'] , [ 'd', 'e', ' f '] , [ 'g'] ]
list2 = [ [ 'x', 'b', 'c'] , [ 'd', 'e', ' f '] , [ 'g’ ] ]

现在来看浅复制,当两个对象通过浅复制进行复制时,两个父对象的子对象引用相同的内存位置,但任何被复制对象的任何进一步的新更改都将彼此独立。 让我们通过一个小例子来理解这一点。假设我们有这样一小段代码:

import copy

list1 = [ [ 'a', 'b', 'c'] , [ 'd', 'e', ' f ']  ]      # assigning a list
list2 = copy.copy(list1)       # shallow copy is done using copy function of copy module

list1.append ( [ 'g', 'h', 'i'] )   # appending another list to list1

print list1
list1 = [ [ 'a', 'b', 'c'] , [ 'd', 'e', ' f '] , [ 'g', 'h', 'i'] ]
list2 = [ [ 'a', 'b', 'c'] , [ 'd', 'e', ' f '] ]

注意,list2不受影响,但如果我们对子对象进行如下更改:

list1[0][0] = 'x’

那么list1和list2都将得到变化:

list1 = [ [ 'x', 'b', 'c'] , [ 'd', 'e', ' f '] , [ 'g', 'h', 'i'] ] 
list2 = [ [ 'x', 'b', 'c'] , [ 'd', 'e', ' f '] ]

现在,深度复制有助于创建彼此完全隔离的对象。如果两个对象通过深度复制进行复制,那么父对象和子对象都将指向不同的内存位置。 例子:

import copy

list1 = [ [ 'a', 'b', 'c'] , [ 'd', 'e', ' f ']  ]         # assigning a list
list2 = deepcopy.copy(list1)       # deep copy is done using deepcopy function of copy module

list1.append ( [ 'g', 'h', 'i'] )   # appending another list to list1

print list1
list1 = [ [ 'a', 'b', 'c'] , [ 'd', 'e', ' f '] , [ 'g', 'h', 'i'] ]
list2 = [ [ 'a', 'b', 'c'] , [ 'd', 'e', ' f '] ]

注意,list2不受影响,但如果我们对子对象进行如下更改:

list1[0][0] = 'x’

list2也不会受到影响,因为所有子对象和父对象都指向不同的内存位置:

list1 = [ [ 'x', 'b', 'c'] , [ 'd', 'e', ' f '] , [ 'g', 'h', 'i'] ] 
list2 = [ [ 'a', 'b', 'c'] , [ 'd', 'e', ' f  ' ] ]

希望能有所帮助。

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

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]

下面的代码演示了赋值、使用复制方法的浅复制、使用(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]))