dee*_*ank 192 python copy immutability deep-copy variable-assignment
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))
Run Code Online (Sandbox Code Playgroud)
我得到以下结果 -
immutable - id(a)==id(a1) True
immutable - id(b)==id(b1) True
mutable - id(c)==id(c1) False
mutable - id(d)==id(d1) False
Run Code Online (Sandbox Code Playgroud)
如果我进行深度扫描 -
a1 = copy.deepcopy(a)
b1 = copy.deepcopy(b)
c1 = copy.deepcopy(c)
d1 = copy.deepcopy(d)
Run Code Online (Sandbox Code Playgroud)
结果是一样的 -
immutable - id(a)==id(a1) True
immutable - id(b)==id(b1) True
mutable - id(c)==id(c1) False
mutable - id(d)==id(d1) False
Run Code Online (Sandbox Code Playgroud)
如果我从事作业操作 -
a1 = a
b1 = b
c1 = c
d1 = d
Run Code Online (Sandbox Code Playgroud)
那么结果是 -
immutable - id(a)==id(a1) True
immutable - id(b)==id(b1) True
mutable - id(c)==id(c1) True
mutable - id(d)==id(d1) True
Run Code Online (Sandbox Code Playgroud)
有人可以解释这些副本究竟有什么区别吗?它是与可变和不可变对象相关的东西吗?如果是这样,你能解释一下吗?
grc*_*grc 342
正常的赋值操作只是将新变量指向现有对象.该文档解释了浅层和深层副本之间的区别:
浅复制和深复制之间的区别仅与复合对象(包含其他对象的对象,如列表或类实例)相关:
浅复制构造一个新的复合对象,然后(尽可能)将对它的引用插入到原始对象中.
深层复制构造一个新的复合对象,然后递归地将复制对象插入到原始对象中找到的对象中.
这是一个小小的示范:
import copy
a = [1, 2, 3]
b = [4, 5, 6]
c = [a, b]
Run Code Online (Sandbox Code Playgroud)
使用正常的赋值操作来复制:
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]
Run Code Online (Sandbox Code Playgroud)
使用浅拷贝:
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]
Run Code Online (Sandbox Code Playgroud)
使用深层副本:
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
Run Code Online (Sandbox Code Playgroud)
per*_*eal 42
对于不可变对象,不需要复制,因为数据永远不会改变,因此Python使用相同的数据; ID总是一样的.对于可变对象,由于它们可能会发生变化,[浅]复制会创建一个新对象.
深层复制与嵌套结构相关.如果您有列表列表,那么copies也要对嵌套列表进行深度复制,因此它是一个递归副本.只需复制,您就有了一个新的外部列表,但内部列表是引用.
作业不复制.它只是设置对旧数据的引用.因此,您需要复制以创建具有相同内容的新列表.
Soh*_*oqi 19
对于不可变对象,创建副本没有多大意义,因为它们不会改变.对于可变对象assignment,copy而deepcopy表现不同.让我们用例子来谈谈它们中的每一个.
赋值操作只是将源的引用分配给目标,例如:
>>> i = [1,2,3]
>>> j=i
>>> hex(id(i)), hex(id(j))
>>> ('0x10296f908', '0x10296f908') #Both addresses are identical
Run Code Online (Sandbox Code Playgroud)
现在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
Run Code Online (Sandbox Code Playgroud)
另一方面copy,deepcopy创建一个新的变量副本.因此,现在对原始变量的更改不会反映到复制变量,反之亦然.但是copy(shallow copy),不要创建嵌套对象的副本,而只是复制嵌套对象的引用.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
Run Code Online (Sandbox Code Playgroud)
嵌套列表示例使用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
Run Code Online (Sandbox Code Playgroud)
平面列表示例使用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
Run Code Online (Sandbox Code Playgroud)
嵌套列表示例使用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
Run Code Online (Sandbox Code Playgroud)
use*_*754 14
让我们在图形示例中看到如何执行以下代码:
import copy
class Foo(object):
def __init__(self):
pass
a = [Foo(), Foo()]
shallow = copy.copy(a)
deep = copy.deepcopy(a)
Run Code Online (Sandbox Code Playgroud)
小智 6
在Python中,当我们将列表、元组、字典等对象分配给另一个通常带有“=”符号的对象时,Python会通过引用创建copy\xe2\x80\x99s创建copy\xe2\x80\x99s 。也就是说,让\xe2\x80\x99s 说我们有一个这样的列表:
\n\nlist1 = [ [ 'a' , 'b' , 'c' ] , [ 'd' , 'e' , 'f' ] ]\nRun Code Online (Sandbox Code Playgroud)\n\n我们将另一个列表分配给这个列表,例如:
\n\nlist2 = list1\nRun Code Online (Sandbox Code Playgroud)\n\n那么如果我们在 python 终端中打印 list2 我们\xe2\x80\x99会得到这个:
\n\nlist2 = [ [ 'a', 'b', 'c'] , [ 'd', 'e', ' f '] ]\nRun Code Online (Sandbox Code Playgroud)\n\nlist1 和 list2 都指向相同的内存位置,对其中任何一个的任何更改都会导致两个对象中可见的更改,即两个对象都指向相同的内存位置。\n如果我们像这样更改 list1 :
\n\nlist1[0][0] = 'x\xe2\x80\x99\nlist1.append( [ 'g'] )\nRun Code Online (Sandbox Code Playgroud)\n\n那么 list1 和 list2 都将是:
\n\nlist1 = [ [ 'x', 'b', 'c'] , [ 'd', 'e', ' f '] , [ 'g'] ]\nlist2 = [ [ 'x', 'b', 'c'] , [ 'd', 'e', ' f '] , [ 'g\xe2\x80\x99 ] ]\nRun Code Online (Sandbox Code Playgroud)\n\n现在进入浅复制,当通过浅拷贝复制两个对象时,两个父对象的子对象引用相同的内存位置,但任何复制对象中的任何进一步的新更改将彼此独立。\nLet\xe2\ x80\x99s 通过一个小例子来理解这一点。假设我们有这个小代码片段:
\n\nimport copy\n\nlist1 = [ [ 'a', 'b', 'c'] , [ 'd', 'e', ' f '] ] # assigning a list\nlist2 = copy.copy(list1) # shallow copy is done using copy function of copy module\n\nlist1.append ( [ 'g', 'h', 'i'] ) # appending another list to list1\n\nprint list1\nlist1 = [ [ 'a', 'b', 'c'] , [ 'd', 'e', ' f '] , [ 'g', 'h', 'i'] ]\nlist2 = [ [ 'a', 'b', 'c'] , [ 'd', 'e', ' f '] ]\nRun Code Online (Sandbox Code Playgroud)\n\n请注意,list2 保持不受影响,但如果我们对子对象进行更改,例如:
\n\nlist1[0][0] = 'x\xe2\x80\x99\nRun Code Online (Sandbox Code Playgroud)\n\n那么 list1 和 list2 都会发生变化:
\n\nlist1 = [ [ 'x', 'b', 'c'] , [ 'd', 'e', ' f '] , [ 'g', 'h', 'i'] ] \nlist2 = [ [ 'x', 'b', 'c'] , [ 'd', 'e', ' f '] ]\nRun Code Online (Sandbox Code Playgroud)\n\n现在,深复制有助于创建彼此完全隔离的对象。如果通过深度复制复制两个对象,则父对象和 it\xe2\x80\x99s 子对象将指向不同的内存位置。\n示例:
\n\nimport copy\n\nlist1 = [ [ 'a', 'b', 'c'] , [ 'd', 'e', ' f '] ] # assigning a list\nlist2 = deepcopy.copy(list1) # deep copy is done using deepcopy function of copy module\n\nlist1.append ( [ 'g', 'h', 'i'] ) # appending another list to list1\n\nprint list1\nlist1 = [ [ 'a', 'b', 'c'] , [ 'd', 'e', ' f '] , [ 'g', 'h', 'i'] ]\nlist2 = [ [ 'a', 'b', 'c'] , [ 'd', 'e', ' f '] ]\nRun Code Online (Sandbox Code Playgroud)\n\n请注意,list2 保持不受影响,但如果我们对子对象进行更改,例如:
\n\nlist1[0][0] = 'x\xe2\x80\x99\nRun Code Online (Sandbox Code Playgroud)\n\n那么 list2 也不会受到影响,因为所有子对象和父对象都指向不同的内存位置:
\n\nlist1 = [ [ 'x', 'b', 'c'] , [ 'd', 'e', ' f '] , [ 'g', 'h', 'i'] ] \nlist2 = [ [ 'a', 'b', 'c'] , [ 'd', 'e', ' f ' ] ]\nRun Code Online (Sandbox Code Playgroud)\n\n希望能帮助到你。
\na,b,c,d,a1,b1,c1和d1是对内存中对象的引用,这些对象由其ID唯一标识。
分配操作将引用内存中的对象,然后将该引用分配给新名称。 c=[1,2,3,4]是一种分配,它创建一个包含这四个整数的新列表对象,并将对该对象的引用分配给c。 c1=c是一个分配,该分配对同一对象引用相同,并分配给c1。由于列表是可变的,因此无论您通过c还是访问列表,该列表上发生的任何事情都是可见的c1,因为它们都引用相同的对象。
c1=copy.copy(c)是一个“浅表副本”,它创建一个新列表,并将对该新列表的引用分配给c1。 c仍然指向原始列表。因此,如果您在修改列表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仍然只包含原始的三个引用。
“深层副本”不仅会复制外部列表,而且还会进入列表内部并复制内部列表,从而使两个结果对象不包含任何相同的引用(就可变对象而言) 。如果内部列表中还有其他列表(或其他对象,如字典),它们也将被复制。那就是“深层副本”的“深层”部分。
| 归档时间: |
|
| 查看次数: |
85404 次 |
| 最近记录: |