• 深浅copy详解


    一. 前言

      在python中,对象的赋值和深浅copy,是有差异的.最终得的值也不同,下面我们就通过几个例子,来看下它们之间的区别.

    二. 赋值

      

     1 list2 = ["jack",22,["65kg","170cm"]]
     2 #对象赋值操作
     3 list3 = list2
     4 print("修改前list2的内存地址:",id(list2))
     5 print("修改前list3的内存地址:",id(list3))
     6 print("修改前list2中元素的内存地址:",[id(x) for x in list2])
     7 print("修改前list3中元素的内存地址:",[id(x) for x in list3])
     8 list2[0] = 1
     9 list2[2][0]="60kg"
    10 
    11 print("修改后list2的内存地址:",id(list2))
    12 print("修改后list3的内存地址:",id(list3))
    13 print("修改后list2中元素的内存地址:",[id(x) for x in list2])
    14 print("修改后list3中元素的内存地址:",[id(x) for x in list3])
    15 16 print("list2:%s"%list2)
    17 print("list3:%s"%list3)
     1 修改前list2的内存地址: 43781448
     2 修改前list3的内存地址: 43781448
     3 修改前list2中元素的内存地址: [43775120, 1389588608, 43782216]
     4 修改前list3中元素的内存地址: [43775120, 1389588608, 43782216]
     5 修改后list2的内存地址: 43781448
     6 修改后list3的内存地址: 43781448
     7 修改后list2中元素的内存地址: [1389587936, 1389588608, 43782216]
     8 修改后list3中元素的内存地址: [1389587936, 1389588608, 43782216]
     9 10 list2:[1, 22, ['60kg', '170cm']]
    11 list3:[1, 22, ['60kg', '170cm']]

      赋值操作:其实是指向了值的内存地址.修改list2[2][0],list3中的数据也跟着变了.这时打印输出list2和list3中[2][0],发现它们指向的都是同一个内存地址.且无论是修改list2还是修改list3

    效果都是一样的.

    三. 浅copy

       

     1 #浅copy,只copy对象和子对象,不会对子对象中的数据进行copy
     2 
     3 list2 = ["jack",22,["65kg","170cm"]]
     4 list3 = list2[:]
     5 # list3 = list2.copy()
     6 print("修改前list2的内存地址:",id(list2))
     7 print("修改前list3的内存地址:",id(list3))
     8 print("修改前list2中元素的内存地址:",[id(x) for x in list2])
     9 print("修改前list3中元素的内存地址:",[id(x) for x in list3])
    10 list2[0] = 1
    11 list2[2][0]="60kg"
    12 list3[2][0] = "70kj"
    13 print("修改后list2的内存地址:",id(list2))
    14 print("修改后list3的内存地址:",id(list3))
    15 print("修改后list2中元素的内存地址:",[id(x) for x in list2])
    16 print("修改后list3中元素的内存地址:",[id(x) for x in list3])
    17 print([id(x) for x in list2])
    18 print("list2:%s"%list2)
    19 print("list3:%s"%list3)
    20 
    21 print(id(list2[2][0]))
    22 print(id(list3[2][0]))
    23 ------------------------------------------------------------------------------------
    24 修改前list2的内存地址: 37485896
    25 修改前list3的内存地址: 37486472
    26 修改前list2中元素的内存地址: [37487704, 1371107456, 37490760]
    27 修改前list3中元素的内存地址: [37487704, 1371107456, 37490760]
    28 修改后list2的内存地址: 37485896
    29 修改后list3的内存地址: 37486472
    30 修改后list2中元素的内存地址: [1371106784, 1371107456, 37490760]
    31 修改后list3中元素的内存地址: [37487704, 1371107456, 37490760]
    32 [1371106784, 1371107456, 37490760]
    33 list2:[1, 22, ['70kj', '170cm']]
    34 list3:['jack', 22, ['70kj', '170cm']]
    35 37509976
    36 37509976

      总结:浅copy,在copy一个对象时,会创建一个新的对象(第24,25即可看出,list2和list3是2个不同的对象),但是子对象的中的(对象)数据依然是引用被copy对象的指向的内存地址的值.

    四. 深copy

      

     1 # 深copy,完整的copy一个对象,生成一个全新的对象.
     2 import copy
     3 list2 = ["jack",22,["65kg","170cm"]]
     4 list3=copy.deepcopy(list2)
     5 print("修改前list2的内存地址:",id(list2))
     6 print("修改前list3的内存地址:",id(list3))
     7 print("修改前list2中元素的内存地址:",[id(x) for x in list2])
     8 print("修改前list3中元素的内存地址:",[id(x) for x in list3])
     9 list2[0] = 1
    10 list2[2][0]="60kg"
    11 list3[2][0] = "70kj"
    12 print("修改后list2的内存地址:",id(list2))
    13 print("修改后list3的内存地址:",id(list3))
    14 print("修改后list2中元素的内存地址:",[id(x) for x in list2])
    15 print("修改后list3中元素的内存地址:",[id(x) for x in list3])
    16 print([id(x) for x in list2])
    17 print("list2:%s"%list2)
    18 print("list3:%s"%list3)
    19 
    20 print(id(list2[2][0]))
    21 print(id(list3[2][0]))
    22 ---------------------------------------------------------------------------------------
    23 修改前list2的内存地址: 43777352
    24 修改前list3的内存地址: 43777928
    25 修改前list2中元素的内存地址: [43779160, 1371107456, 43782216]
    26 修改前list3中元素的内存地址: [43779160, 1371107456, 43777864]
    27 修改后list2的内存地址: 43777352
    28 修改后list3的内存地址: 43777928
    29 修改后list2中元素的内存地址: [1371106784, 1371107456, 43782216]
    30 修改后list3中元素的内存地址: [43779160, 1371107456, 43777864]
    31 [1371106784, 1371107456, 43782216]
    32 list2:[1, 22, ['60kg', '170cm']]
    33 list3:['jack', 22, ['70kj', '170cm']]
    34 43801376
    35 43801432

      深copy:会创建一个全新的对象.在未对数据进行修改的情况下,默认2个对象的元素(不可变类型)指向的是一个内存地址(但是不包括可变的类型的子对象,可变类型会重新创建一个开辟一个新的内存空间,用来存放自己的数据).下面代码中,前4个元素都是不可变类型,所以在2个列表中,指向的都是同一个内存地址.后面可变的数据类型的元素,list3都重新创建了新的内存空间来存放数据. 不可变类型修改时,会重新开辟新的内存空间,不在指向原来的内存空间.

     1 import copy
     2 list2 = ["jack",22,("a","b"),True,["65kg","170cm"],{"k1":"v1"},{1,2,3,4}]
     3 list3=copy.deepcopy(list2)
     4 print("list2中元素的内存地址:",[id(x) for x in list2])
     5 print("list3中元素的内存地址:",[id(x) for x in list3])
     6 print("list2:%s"%list2)
     7 print("list3:%s"%list3)
     8 
     9 print(id(list2[4][0]))      #查看第5个元素中的子元素的内存地址
    10 print(id(list3[4][0]))
    11 
    12 --------------------------------------------------------------------------------------------
    13 list2中元素的内存地址: [37418128, 495874176, 37427208, 495382752, 37424840, 31333400, 37414952]
    14 list3中元素的内存地址: [37418128, 495874176, 37427208, 495382752, 37421192, 32129888, 37416072]
    15 list2:['jack', 22, ('a', 'b'), True, ['65kg', '170cm'], {'k1': 'v1'}, {1, 2, 3, 4}]
    16 list3:['jack', 22, ('a', 'b'), True, ['65kg', '170cm'], {'k1': 'v1'}, {1, 2, 3, 4}]
    17 37418408
    18 37418408

      特殊情况:如果元祖中包含的是一个可变的数据类型(字典,集合,列表),则不能进行深copy.(而是新开辟内存空间来存放)

      

     1 import copy
     2 list2 = [([10,12,13])]
     3 list3=copy.deepcopy(list2)
     4 print("list2中元素的内存地址:",[id(x) for x in list2])
     5 print("list3中元素的内存地址:",[id(x) for x in list3])
     6 print("list2:%s"%list2)
     7 print("list3:%s"%list3)
     8 --------------------------------------------------------------------------------------------
     9 list2中元素的内存地址: [43515592]
    10 list3中元素的内存地址: [43511944]
    11 list2:[[10, 12, 13]]
    12 list3:[[10, 12, 13]]

    五. 深浅copy总结

    • Python中对象的赋值都是进行对象引用(内存地址)传递
    • 使用copy.copy(),可以进行对象的浅拷贝,它复制了对象,但对于对象中的元素,依然使用原始的引用.
    • 如果需要复制一个容器对象,以及它里面的所有元素(包含元素的子元素),可以使用copy.deepcopy()进行深拷贝
    • 对于非容器类型(如数字、字符串、和其他'原子'类型的对象)没有被拷贝一说,都是重新开辟内存空间来存放数据
    • 如果元祖变量只包含原子类型对象,则不能深拷贝

      

  • 相关阅读:
    Redis string
    java 是 传值还是传址 Pass-by-value or Pass-by-reference
    IDEA 适用技巧
    测试 MD
    pyqt5 学习总结
    win10 安装anaconda 无法使用pip 报错缺少SSL模块
    Hadoop datanode无法启动
    Ansible 安装jdk
    java 安装后 不能 java javac 说找不到命令 -bash: javac: command not found
    如何去掉MapReduce输出的默认分隔符
  • 原文地址:https://www.cnblogs.com/lovepy3/p/9274970.html
Copyright © 2020-2023  润新知