问题:浅表复制,深度复制和常规分配操作之间有什么区别?

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

有人可以解释究竟是什么造成了副本之间的差异吗?它与可变且不可变的对象有关吗?如果是这样,请您向我解释一下?

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))

I get the following results:

immutable - id(a)==id(a1) True
immutable - id(b)==id(b1) True
mutable - id(c)==id(c1) False
mutable - id(d)==id(d1) False

If I perform deepcopy:

a1 = copy.deepcopy(a)
b1 = copy.deepcopy(b)
c1 = copy.deepcopy(c)
d1 = copy.deepcopy(d)

results are the same:

immutable - id(a)==id(a1) True
immutable - id(b)==id(b1) True
mutable - id(c)==id(c1) False
mutable - id(d)==id(d1) False

If I work on assignment operations:

a1 = a
b1 = b
c1 = c
d1 = d

then results are:

immutable - id(a)==id(a1) True
immutable - id(b)==id(b1) True
mutable - id(c)==id(c1) True
mutable - id(d)==id(d1) True

Can somebody explain what exactly makes a difference between the copies? Is it something related to mutable & immutable objects? If so, can you please explain it to me?


回答 0

普通赋值操作将简单地将新变量指向现有对象。该文档解释了浅拷贝和深拷贝之间的区别:

浅复制和深复制之间的区别仅与复合对象(包含其他对象的对象,如列表或类实例)有关:

  • 浅表副本将构造一个新的复合对象,然后(在可能的范围内)将对原始对象中找到的对象的引用插入其中。

  • 深层副本将构造一个新的复合对象,然后递归地将原始对象中发现的对象的副本插入其中。

这是一个小示范:

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

Normal assignment operations will simply point the new variable towards the existing object. The docs explain the difference between shallow and deep copies:

The difference between shallow and deep copying is only relevant for compound objects (objects that contain other objects, like lists or class instances):

  • A shallow copy constructs a new compound object and then (to the extent possible) inserts references into it to the objects found in the original.

  • A deep copy constructs a new compound object and then, recursively, inserts copies into it of the objects found in the original.

Here’s a little demonstration:

import copy

a = [1, 2, 3]
b = [4, 5, 6]
c = [a, b]

Using normal assignment operatings to copy:

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]

Using a shallow copy:

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]

Using a deep copy:

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

回答 1

对于不可变的对象,不需要复制,因为数据永远不会改变,因此Python使用相同的数据。id始终相同。对于可变对象,由于它们可能会更改,因此[shallow]复制会创建一个新对象。

深层复制与嵌套结构有关。如果您有列表列表,则还深度copies复制嵌套列表,因此它是递归副本。仅使用复制,您就有一个新的外部列表,但是内部列表是引用。

作业不会复制。它只是将参考设置为旧数据。因此,您需要复制以创建具有相同内容的新列表。

For immutable objects, there is no need for copying because the data will never change, so Python uses the same data; ids are always the same. For mutable objects, since they can potentially change, [shallow] copy creates a new object.

Deep copy is related to nested structures. If you have list of lists, then deepcopy copies the nested lists also, so it is a recursive copy. With just copy, you have a new outer list, but inner lists are references.

Assignment does not copy. It simply sets the reference to the old data. So you need copy to create a new list with the same contents.


回答 2

对于不可变的对象,创建副本没有多大意义,因为它们不会更改。对于可变对象assignmentcopydeepcopy表现不同。让我们通过示例来讨论它们。

分配操作仅将源的引用分配给目标,例如:

>>> i = [1,2,3]
>>> j=i
>>> hex(id(i)), hex(id(j))
>>> ('0x10296f908', '0x10296f908') #Both addresses are identical

现在i,从j技术上讲是指相同的列表。两者ij具有相同的内存地址。对其中任何一个的任何更新都会反映到另一个。例如:

>>> i.append(4)
>>> j
>>> [1,2,3,4] #Destination is updated

>>> j.append(5)
>>> i
>>> [1,2,3,4,5] #Source is updated

另一方面copydeepcopy创建变量的新副本。因此,现在对原始变量所做的更改将不会反映到复制变量中,反之亦然。但是copy(shallow copy),不要创建嵌套对象的副本,而只是复制嵌套对象的引用。Deepcopy递归复制所有嵌套对象。

一些示例来演示copyand的行为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    

For immutable objects, creating a copy don’t make much sense since they are not going to change. For mutable objects assignment,copy and deepcopy behaves differently. Lets talk about each of them with examples.

An assignment operation simply assigns the reference of source to destination e.g:

>>> i = [1,2,3]
>>> j=i
>>> hex(id(i)), hex(id(j))
>>> ('0x10296f908', '0x10296f908') #Both addresses are identical

Now i and j technically refers to same list. Both i and j have same memory address. Any updation to either of them will be reflected to the other. e.g:

>>> i.append(4)
>>> j
>>> [1,2,3,4] #Destination is updated

>>> j.append(5)
>>> i
>>> [1,2,3,4,5] #Source is updated

On the other hand copy and deepcopy creates a new copy of variable. So now changes to original variable will not be reflected to the copy variable and vice versa. However copy(shallow copy), don’t creates a copy of nested objects, instead it just copies the reference of nested objects. Deepcopy copies all the nested objects recursively.

Some examples to demonstrate behaviour of copy and deepcopy:

Flat list example using 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

Nested list example using 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

Flat list example using 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

Nested list example using 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    

回答 3

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

import copy

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


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

在此处输入图片说明

Let’s see in a graphical example how the following code is executed:

import copy

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


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

enter image description here


回答 4

a,b,c,d,a1,b1,c1和d1是对内存中对象的引用,这些对象由其ID唯一标识。

分配操作将引用内存中的对象,然后将该引用分配给新名称。 c=[1,2,3,4]是一种分配,它创建一个包含这四个整数的新列表对象,并将对该对象的引用分配给cc1=c是一种分配,该分配对同一对象引用相同,并分配给c1。由于列表是可变的,因此无论您通过c还是访问列表,该列表上发生的任何事情都将可见c1,因为它们都引用相同的对象。

c1=copy.copy(c)是一个“浅表副本”,它创建一个新列表,并将对该新列表的引用分配给c1c仍然指向原始列表。因此,如果您在修改列表c1,则c引用的列表将不会更改。

复制的概念与诸如整数和字符串之类的不可变对象无关。由于您无法修改这些对象,因此永远不需要在不同位置的内存中拥有两个具有相同值的副本。因此,简单地重新分配了整数和字符串以及其他不适用复制概念的对象。这就是为什么带有a和的示例b导致相同ID的原因。

c1=copy.deepcopy(c)是“深层副本”,但在此示例中,其功能与浅层副本相同。深层副本与浅层副本的不同之处在于,浅层副本将创建对象本身的新副本,但是该对象内部的任何引用都不会被自己复制。在您的示例中,列表中仅包含整数(它们是不可变的),并且如前所述,无需复制这些整数。因此,深层副本的“深层”部分不适用。但是,请考虑以下更复杂的列表:

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仍然只包含原始的三个引用。

“深层副本”不仅会复制外部列表,还会在列表内部复制内部列表,从而使两个结果对象不包含任何相同的引用(就可变对象而言) 。如果内部列表中还有其他列表(或其他对象,如字典),它们也将被复制。那就是“深复制”的“深”部分。

a, b, c, d, a1, b1, c1 and d1 are references to objects in memory, which are uniquely identified by their ids.

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) is a “shallow copy” that creates a new list and assigns the reference to the new list to c1. c still points to the original list. So, if you modify the list at c1, the list that c refers to will not change.

The concept of copying is irrelevant to immutable objects like integers and strings. Since you can’t modify those objects, there is never a need to have two copies of the same value in memory at different locations. So integers and strings, and some other objects to which the concept of copying does not apply, are simply reassigned. This is why your examples with a and b result in identical ids.

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]]

This is a list that contains other lists (you could also describe it as a two-dimensional array).

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.

A ‘deep copy’ would not only duplicate the outer list, but it would also go inside the lists and duplicate the inner lists, so that the two resulting objects do not contain any of the same references (as far as mutable objects are concerned). If the inner lists had further lists (or other objects such as dictionaries) inside of them, they too would be duplicated. That’s the ‘deep’ part of the ‘deep copy’.


回答 5

在python中,当我们将列表,元组,字典等对象分配给通常带有’=’符号的另一个对象时,python将通过引用创建副本。也就是说,假设我们有一个像这样的列表列表:

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 '] ]

现在,深层复制有助于彼此之间创建完全隔离的对象。如果通过Deep Copy复制了两个对象,则父对象及其子对象都将指向不同的存储位置。范例:

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  ' ] ]

希望能帮助到你。

In python, when we assign objects like list, tuples, dict, etc to another object usually with a ‘ = ‘ sign, python creates copy’s by reference. That is, let’s say we have a list of list like this :

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

and we assign another list to this list like :

list2 = list1

then if we print list2 in python terminal we’ll get this :

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

Both list1 & list2 are pointing to same memory location, any change to any one them will result in changes visible in both objects, i.e both objects are pointing to same memory location. If we change list1 like this :

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

then both list1 and list2 will be :

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

Now coming to Shallow copy, when two objects are copied via shallow copy, the child object of both parent object refers to same memory location but any further new changes in any of the copied object will be independent to each other. Let’s understand this with a small example. Suppose we have this small code snippet :

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 '] ]

notice, list2 remains unaffected, but if we make changes to child objects like :

list1[0][0] = 'x’

then both list1 and list2 will get change :

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

Now, Deep copy helps in creating completely isolated objects out of each other. If two objects are copied via Deep Copy then both parent & it’s child will be pointing to different memory location. Example :

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 '] ]

notice, list2 remains unaffected, but if we make changes to child objects like :

list1[0][0] = 'x’

then also list2 will be unaffected as all the child objects and parent object points to different memory location :

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

Hope it helps.


回答 6

下面的代码演示了赋值,使用copy方法的浅表副本,使用(slice)[:]的浅表副本和Deepcopy之间的区别。下面的示例通过使差异更明显来使用嵌套列表。

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]))

Below code demonstrates the difference between assignment, shallow copy using the copy method, shallow copy using the (slice) [:] and the deepcopy. Below example uses nested lists there by making the differences more evident.

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]))

回答 7

要采取的GIST是这样的:在创建浅表时,使用“常规分配”处理浅表(没有sub_list,仅单个元素)会产生“副作用”,然后使用“常规分配”创建此列表的副本。当您更改创建的副本列表的任何元素时,这种“副作用”是因为它会自动更改原始列表的相同元素。那是copy方便的,因为它在更改复制元素时不会更改原始列表元素。

另一方面,copy当您有一个包含列表的列表(sub_lists)并deepcopy解决该列表时,确实也会产生“副作用” 。例如,如果您创建一个包含嵌套列表的大列表(sub_lists),然后创建此大列表的副本(原始列表)。当您修改副本列表的sub_lists时,将自动修改大列表的sub_lists,从而产生“副作用”。有时(在某些项目中)您希望不修改就按原样保留大列表(原始列表),而您想要做的只是复制其元素(sub_lists)。为此,您的解决方案是使用deepcopy它将解决这种“副作用”并在不修改原始内容的情况下进行复制的方法。

copydeep copy操作的不同行为仅涉及复合对象(即:包含其他对象(如列表)的对象)。

这是此简单代码示例中说明的差异:

第一

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

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

现在,让我们运行一些print测试,看看原始列表与其副本列表相比如何:

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子元素会自动修改copy_list子元素

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子元素会自动修改original_list子元素

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)

现在,让我们运行一些print测试,看看原始列表与其副本列表相比如何:

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]

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.

The different behaviors of copy and deep copy operations concerns only compound objects (ie: objects that contain other objects such as lists).

Here are the differences illustrated in this simple code example:

First

let’s check how copy (shallow) behaves, by creating an original list and a copy of this list:

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

Now, let’s run some print tests and see how the original list behave compared to its copy list:

original_list and copy_list have different addresses

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

elements of original_list and copy_list have the same addresses

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

sub_elements of original_list and copy_list have the same addresses

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

modifying original_list elements does NOT modify copy_list elements

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']]

modifying copy_list elements does NOT modify original_list elements

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]

modifying original_list sub_elements automatically modify 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]

modifying copy_list sub_elements automatically modify 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]

Second

let’s check how deepcopy behaves, by doing the same thing as we did with copy (creating an original list and a copy of this list):

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

Now, let’s run some print tests and see how the original list behave compared to its copy list:

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

original_list and copy_list have different addresses

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

elements of original_list and copy_list have the same addresses

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

sub_elements of original_list and copy_list have different addresses

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

modifying original_list elements does NOT modify copy_list elements

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']]

modifying copy_list elements does NOT modify original_list elements

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]

modifying original_list sub_elements does NOT modify 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]

modifying copy_list sub_elements does NOT modify 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]

回答 8

不知道上面是否提到了它,但是要理解.copy()创建对原始对象的引用是非常重要的。如果更改复制的对象-则更改原始对象。.deepcopy()创建新对象,并将原始对象真正复制到新对象。更改新的深层复制对象不会影响原始对象。

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

因此,.copy()和.deepcopy()之间的复制/引用差异很大。

Not sure if it mentioned above or not, but it’s very importable to undestand that .copy() create reference to original object. If you change copied object – you change the original object. .deepcopy() creates new object and does real copying of original object to new one. Changing new deepcopied object doesn’t affect original object.

And yes, .deepcopy() copies original object recursively, while .copy() create a reference object to first-level data of original object.

So the copying/referencing difference between .copy() and .deepcopy() is significant.


回答 9

深层复制与嵌套结构有关。如果您有列表列表,则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]复制方法将外部列表的内容复制到新列表,但内部列表为两个列表仍然相同,因此,如果您在任何列表的内部列表中进行更改,都会影响两个列表。

但是,如果您使用Deep copy,那么它也会为内部列表创建新实例。

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]

Deep copy is related to nested structures. If you have list of lists, then deepcopy copies the nested lists also, so it is a recursive copy. With just copy, you have a new outer list, but inner lists are references. Assignment does not copy. For Ex

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

OutPut

[[0, 1, 2, 3, 3], 4, 5] [[0, 1, 2, 3, 3], 4, 5, 3] Copy method copy content of outer list to new list but inner list is still same for both list so if you make changes in inner list of any lists it will affects both list.

But if you use Deep copy then it will create new instance for inner list too.

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

Output

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


回答 10

>>lst=[1,2,3,4,5]

>>a=lst

>>b=lst[:]

>>> b
[1, 2, 3, 4, 5]

>>> a
[1, 2, 3, 4, 5]

>>> lst is b
False

>>> lst is a
True

>>> id(lst)
46263192

>>> id(a)
46263192 ------>  See here id of a and id of lst is same so its called deep copy and even boolean answer is true

>>> id(b)
46263512 ------>  See here id of b and id of lst is not same so its called shallow copy and even boolean answer is false although output looks same.
>>lst=[1,2,3,4,5]

>>a=lst

>>b=lst[:]

>>> b
[1, 2, 3, 4, 5]

>>> a
[1, 2, 3, 4, 5]

>>> lst is b
False

>>> lst is a
True

>>> id(lst)
46263192

>>> id(a)
46263192 ------>  See here id of a and id of lst is same so its called deep copy and even boolean answer is true

>>> id(b)
46263512 ------>  See here id of b and id of lst is not same so its called shallow copy and even boolean answer is false although output looks same.

声明:本站所有文章,如无特殊说明或标注,均为本站原创发布。任何个人或组织,在未征得本站同意时,禁止复制、盗用、采集、发布本站内容到任何网站、书籍等各类媒体平台。如若本站内容侵犯了原著者的合法权益,可联系我们进行处理。