【问题标题】:What is the difference between shallow copy, deepcopy and normal assignment operation?浅拷贝、深拷贝和普通赋值操作有什么区别?
【发布时间】:2013-06-19 06:15:42
【问题描述】:
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

有人能解释一下究竟是什么让副本之间有所不同吗?它与可变和不可变对象有关吗?如果是这样,你能解释一下吗?

【问题讨论】:

标签: python copy variable-assignment immutability deep-copy


【解决方案1】:

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

浅拷贝和深拷贝的区别只与 复合对象(包含其他对象的对象,如列表或 类实例):

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

  • 深拷贝构造一个新的复合对象,然后递归地将在 原创。

这里有一个小示范:

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

【讨论】:

  • 分配和浅拷贝一样吗?
  • @Dshank 否。浅拷贝构造一个新对象,而赋值只会将新变量指向现有对象。对现有对象的任何更改都会影响这两个变量(通过赋值)。
  • @grc "对现有对象的任何更改都会影响两个变量(带赋值)" - 此语句仅适用于可变对象,不适用于字符串、浮点数、元组等不可变类型。
  • @grc 但是我已经尝试了一个例子(我在这里删除了新行。)list_=[[1,2],[3,4]] newlist = list_.copy() list_[0]=[7,8] print(list_) print(newlist) newlist 仍然显示[[1, 2], [3, 4]]。但是list_[0] 是一个可变的列表。
  • @Neerav:对于不可变对象也是如此。对不可变对象的任何更改都将通过这两个变量显示出来,因为您无法更改不可变对象 - 该语句对于不可变对象来说是空洞的。
【解决方案2】:

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

深拷贝与嵌套结构有关。如果您有列表列表,那么也可以深度复制copies 嵌套列表,因此它是一个递归副本。只需复制,您就有一个新的外部列表,但内部列表是引用。

分配不复制。它只是设置对旧数据的引用。所以你需要复制来创建一个具有相同内容的新列表。

【讨论】:

  • With just copy, you have a new outer list but inner lists are references. 对于内部列表,复制的会受到原始列表的影响吗?我创建了一个像list_=[[1,2],[3,4]] newlist = list_.copy() list_[0]=[7,8] 这样的列表列表,而newlist 保持不变,那么内部列表是引用吗?
  • @Stallman 您没有在此处更改引用列表,只是创建一个新列表并将其分配为其中一个副本的第一项。尝试做list_[0][0] = 7
【解决方案3】:

对于不可变对象,创建副本没有多大意义,因为它们不会改变。对于可变对象assignmentcopydeepcopy 的行为不同。让我们用例子来讨论它们。

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

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

现在ij 在技术上指的是同一个列表。 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 递归地复制所有嵌套对象。

展示copydeepcopy行为的一些例子:

使用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    

【讨论】:

    【解决方案4】:

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

    import copy
    
    class Foo(object):
        def __init__(self):
            pass
    
    
    a = [Foo(), Foo()]
    shallow = copy.copy(a)
    deep = copy.deepcopy(a)
    

    【讨论】:

    【解决方案5】:

    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所指的列表不会改变。

    复制的概念与整数和字符串等不可变对象无关。由于您无法修改这些对象,因此永远不需要在不同位置的内存中拥有两个相同值的副本。所以整数和字符串,以及其他一些不适用复制概念的对象,只是简单地重新分配。这就是为什么您使用 ab 的示例会产生相同的 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 仍然只包含原来的三个引用。

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

    【讨论】:

      【解决方案6】:

      在python中,当我们将list、tuples、dict等对象分配给通常带有'='符号的另一个对象时,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’ ] ]
      

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

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

      希望对你有帮助。

      【讨论】:

        【解决方案7】:

        深拷贝与嵌套结构有关。如果您有列表列表,则 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] 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]

        【讨论】:

          【解决方案8】:

          下面的代码演示了赋值、浅拷贝使用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]))
          

          【讨论】:

            【解决方案9】:

            要采用的 GIST 是这样的: 当您创建一个浅表列表然后使用“正常分配”创建该列表的副本时,使用“正常分配”处理浅表(没有子列表,只有单个元素)会产生“副作用”。这种“副作用”是当您更改创建的副本列表的任何元素时,因为它会自动更改原始列表的相同元素。这时候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 的子元素地址相同

            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)
            

            现在,让我们运行一些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 的子元素地址不同

            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]
            

            【讨论】:

              【解决方案10】:

              以下代码显示了底层地址在复制、深度复制和分配中如何受到影响。这类似于 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>
              

              【讨论】:

                【解决方案11】:
                >>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.
                

                【讨论】:

                • a 不是lst 的深拷贝!
                【解决方案12】:

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

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

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

                【讨论】:

                • 其他答案中已经提到并解释了。
                猜你喜欢
                • 2010-09-16
                • 1970-01-01
                • 2016-05-07
                • 2012-04-12
                • 2023-04-05
                • 2015-01-13
                • 1970-01-01
                相关资源
                最近更新 更多