• 深拷贝和浅拷贝


    深拷贝和浅拷贝

    铺垫知识:

    Python3中,有6个标准的数据类型,他们又分为可变和不可变。

    不可变数据(3个):

    • Number(数字)
    • String(字符串)
    • Tuple(元组)

    可变数据(3个):

    • List(列表)
    • Dictionary(字典)
    • Set(集合)

    浅拷贝和深度拷贝总结

    浅拷贝

    copy模块里面的copy方法实现

    1. 对于不可变类型 Number、String、Tuple,浅复制仅仅是地址指向,不会开辟新空间。
    2. 列表项对于可变类型List、Dictionary、Set,浅复制会开辟新的空间地址(仅仅是最顶层开辟了新的空间,里层的元素地址还是一样的),进行浅拷贝
    3. 浅拷贝后,改变原始对象中为可变类型的元素的值,会同时影响拷贝对象的;改变原始对象中为不可变类型的元素的值,只有原始类型受影响。(操作拷贝对象对原始对象的也是同理)

    深拷贝

    copy模块里面的deepcopy方法实现

    1. 深拷贝,除了顶层拷贝,还对子元素也进行了拷贝(本质上递归浅拷贝)
    2. 经过深拷贝后,原始对象和拷贝对象所有的元素地址都没有相同的了
    3. 可以用切片表达式进行浅拷贝
    4. 字典的copy方法可以拷贝一个字典
    5. 深拷贝内部子元素如果是不可变类型,但内部子元素的子元素存在可变类型,则按照可变类型的递归浅拷贝对子元素进行拷贝

    可变类型和不可变类型在浅拷贝中的区别

    import copy
    
    # 不可变类型 Number String Tuple
    print("对于不可 变类型 Number String Tuple,浅复制仅仅是地址指向,不会开辟新空间拷贝值")
    
    num1 = 17
    num2 = copy.copy(num1)
    
    print("num1:" + str(id(num1)))
    print("num2:" + str(id(num1)))
    # num1和num2的地址都相同
    
    
    str1 = "hello"
    str2 = copy.copy(str1)
    print("str1:" + str(id(str1)))
    print("str2:" + str(id(str2)))
    # str1和str2的地址都相同
    
    tup1 = (18, "tom")
    tup2 = copy.copy(tup1)
    print("tup1:" + str(id(tup1)))
    print("tup2:" + str(id(tup2)))
    # tup1和tup2的地址都相同
    
    print("="*20)
    print("对于可变类型 List、Dictionary、Set,浅复制会开辟新的空间地址(仅仅是最顶层开辟了新的空间),进行浅拷贝")
    
    
    list1 = [11,12]
    list2 = copy.copy(list1)
    print("list1:" + str(id(list1)))
    print("list2:" + str(id(list2)))
    # list1和list2的地址不相同
    
    
    dic1 = [11,12,"hi"]
    dic2 = copy.copy(dic1)
    print("dic1:" + str(id(dic1)))
    print("dic2:" + str(id(dic2)))
    # dic1和dic2的地址不相同
    
    set1 = {"AA","BB"}
    set2 = copy.copy(set1)
    print("set1:" + str(id(set1)))
    print("set2:" + str(id(set2)))
    # set1和set2的地址不相同
    

    输出:

    对于不可 变类型 Number String Tuple,浅复制仅仅是地址指向,不会开辟新空间拷贝值
    num1:4449693616
    num2:4449693616
    str1:4452098488
    str2:4452098488
    tup1:4451942472
    tup2:4451942472
    ====================
    对于可变类型 List、Dictionary、Set,浅复制会开辟新的空间地址,进行浅拷贝
    list1:4456844424
    list2:4452360136
    dic1:4452358856
    dic2:4456844744
    set1:4452279016
    set2:4452279464
    
    
    对list进浅拷贝,对可变类型和不可变类型修改后的影响。
    import copy
    
    l1 = [11, 12]
    l2 = [21, 22]
    num = 555
    
    allOne = [l1, l2,num]
    
    
    # 浅拷贝,创建出一个对象,并把旧对象元素的 引用地址 拷贝到新对象当中。
    # 也就是说,两个对象里面的元素通过浅拷贝指向的还是同一个地址
    allOne2 = copy.copy(allOne)
    
    l1[0] = 16 # 此处修改,会使得 allOne 和 allOne2的第0个元素的值都发生改变,因为l1是List,是可变对象
    allOne[2] = 666 # 此处修改,只会allOne的num的值,因为不可变对象一旦重新复制,地址就会发生改变。(不可变嘛)
    
    num = 777 # 此处不会改变 allOne 和 allOne2的值,因为相当于 777 复制给一个全新的地址,这个num跟其他num已经没关系了
    
    print(allOne)
    print(allOne2)
    
    print("id allOne:"+str(id(allOne)))
    print("id allOne[0]:"+str(id(allOne[0])))
    print("id allOne[1]:"+str(id(allOne[1])))
    print("id allOne[2]:"+str(id(allOne[2])))
    
    print("===")
    print("id allOne2:"+str(id(allOne2)))
    print("id allOne2[0]:"+str(id(allOne2[0])))
    print("id allOne2[1]:"+str(id(allOne2[1])))
    print("id allOne2[2]:"+str(id(allOne2[2])))
    
    

    打印输出

    [[16, 12], [21, 22], 666]
    [[16, 12], [21, 22], 555]
    id allOne:4467341640
    id allOne[0]:4471819912
    id allOne[1]:4467342920
    id allOne[2]:4466847696
    ===
    id allOne2:4471820232
    id allOne2[0]:4471819912
    id allOne2[1]:4467342920
    id allOne2[2]:4466081744
    

    可以看出:

    1. 改动allOne中的可变类型,会影响allOne2,改变allOne2同理影响allOne。
    2. 改动allOne2中的不可变类型,只有allOne2自身会改变,allOne不受影响。

    对于不可变类型被修改后造成的影响,我们用一个更加简单的例子便可更好理解:

    num = 123
    print(str(id(num)))
    
    num = 666
    print(str(id(num)))
    

    console:

    4348603632
    4350009296
    

    深拷贝对6种基本类型的影响

    我们对3种可变类型3种不可变类型进行深拷贝。
    结果发现,和浅拷贝几乎一致。
    其实这也好理解,因为深拷贝对比浅拷贝,强调的是递归,强调的是子元素。
    对顶层的操作,深浅拷贝无异。

    import copy
    
    # 不可变类型 Number String Tuple
    print("对于不可 变类型 Number String Tuple,深复制依然是地址指向,不会开辟新空间拷贝值")
    
    num1 = 17
    num2 = copy.deepcopy(num1) # 深拷贝
    
    print("num1:" + str(id(num1)))
    print("num2:" + str(id(num1)))
    # num1和num2的地址都相同
    
    
    str1 = "hello"
    str2 = copy.deepcopy(str1)  # 深拷贝
    print("str1:" + str(id(str1)))
    print("str2:" + str(id(str2)))
    # str1和str2的地址都相同
    
    tup1 = (18, "tom")
    tup2 = copy.deepcopy(tup1) # 深拷贝
    print("tup1:" + str(id(tup1)))
    print("tup2:" + str(id(tup2)))
    # tup1和tup2的地址都相同
    
    print("="*20)
    print("对于可变类型 List、Dictionary、Set,深拷贝会开辟新的空间地址,进行拷贝")
    
    
    list1 = [11,12]
    list2 = copy.deepcopy(list1) # 深拷贝
    print("list1:" + str(id(list1)))
    print("list2:" + str(id(list2)))
    # list1和list2的地址不相同
    
    
    dic1 = [11,12,"hi"]
    dic2 = copy.deepcopy(dic1) # 深拷贝
    print("dic1:" + str(id(dic1)))
    print("dic2:" + str(id(dic2)))
    # dic1和dic2的地址不相同
    
    set1 = {"AA","BB"}
    set2 = copy.deepcopy(set1) # 深拷贝
    print("set1:" + str(id(set1)))
    print("set2:" + str(id(set2)))
    # set1和set2的地址不相同
    

    浅拷贝的会对子元素也进行拷贝

    import copy
    
    l1 = [11, 12]
    l2 = [21, 22]
    num = 555
    
    allOne = [l1, l2,num]
    
    
    # 深拷贝,除了顶层拷贝,还对子元素也进行了拷贝(本质上递归浅拷贝)
    # 经过深拷贝后,原始对象和拷贝对象所有的元素地址都没有相同的了
    
    allOne2 = copy.deepcopy(allOne) # copy.deepcopy 深拷贝
    
    allOne[1] = [113,114]
    allOne2[2] = [227,228]
    
    print(allOne)
    print(allOne2)
    
    print("id allOne:"+str(id(allOne)))
    print("id allOne[0]:"+str(id(allOne[0])))
    print("id allOne[1]:"+str(id(allOne[1])))
    print("id allOne[2]:"+str(id(allOne[2])))
    
    print("===")
    print("id allOne2:"+str(id(allOne2)))
    print("id allOne2[0]:"+str(id(allOne2[0])))
    print("id allOne2[1]:"+str(id(allOne2[1])))
    print("id allOne2[2]:"+str(id(allOne2[2])))
    

    console

    [[11, 12], [113, 114], 555]
    [[11, 12], [21, 22], [227, 228]]
    id allOne:4549589640
    id allOne[0]:4554067720
    id allOne[1]:4554067848
    id allOne[2]:4548329424
    ===
    id allOne2:4554067912
    id allOne2[0]:4554067784
    id allOne2[1]:4554067592
    id allOne2[2]:4554100808
    

    本例是跟浅拷贝做对比的。
    在之前的浅拷贝中,子元素是不会开辟新空间做拷贝的。
    而在深拷贝中,子元素也进行了拷贝。

    其他拷贝方式
    除了copy模块的中的copy和deepcopy,还有其他自带的方式可实现拷贝。

    1. 分片表达式进行浅拷贝
    2. 字典的copy方法可以拷贝一个字典

    分片表达式拷贝

    l1 = [11, 12]
    l2 = [21, 22]
    num = 555
    orgi = [l1, l2, num]
    nList = orgi[:]
    print("orgi:"+str(id(orgi)))
    print("orgi[0]:"+str(id(orgi[0])))
    print("orgi[1]:"+str(id(orgi[1])))
    print("orgi[2]:"+str(id(orgi[2])))
    print("*"*30)
    print("nList:"+str(id(nList)))
    print("nList[0]:"+str(id(nList[0])))
    print("nList[1]:"+str(id(nList[1])))
    print("nList[2]:"+str(id(nList[2])))
    

    用分片表达式进行的拷贝,是浅拷贝。
    字典自带的copy方法可实现深拷贝

    dic = {"key": "hello", "num": 18}
    
    dic2 = dic.copy()
    
    dic["key"] = "one"
    dic2["key"] = "two"
    
    print(dic)
    print("dic:" + str(id(dic)))
    
    print(dic2)
    print("dic2:" + str(id(dic2)))
    
    console:
    {'key': 'one', 'num': 18}
    dic:4382946792
    {'key': 'two', 'num': 18}
    dic2:4382946864
    

    如有问题可联系QQ:2499578824
  • 相关阅读:
    小米手机miui8.5连接电脑
    js数组map方法
    wxui入门
    动画函数封装
    系列属性(offset、scroll、client)
    定时器( setInterval和 setTimeout)
    BOM(浏览器对象模型)
    事件(绑定、解绑、冒泡)
    元素(element)创建
    节点(node)操作
  • 原文地址:https://www.cnblogs.com/chunbo/p/11208977.html
Copyright © 2020-2023  润新知