• [ python ] 深浅拷贝


    首先要了解什么是拷贝、浅拷贝、深拷贝?

    拷贝:

        从原始数据复制一份出来,当复制成功后,这两份数据都是相互独立的,即修改任意一份数据都不会影响另一份数据。

    浅拷贝:

        python中,浅拷贝就是只是拷贝最外层的类型,简单来讲就是拷贝了引用,并没有拷贝内容. copy.copy()

    深拷贝:

        对于一个对象所有层次的拷贝(递归拷贝)copy.deepcopy()

    要知道深浅拷贝的区别,首先要知道python中什么是 可变数据类型 和 不可变数据类型

    不可变数据类型的定义:

        python中的不可变数据类型,不允许变量的值发生变化,如果改变了变量的值,相当于是新建了一个对象,而对于相同的值的对象,在内存中则只有一个对象,内部会有一个引用计数来记录有多少个变量引用这个对象.

        python中 不可变数据类型:

    •   整型
    •   浮点数
    •   布尔值
    •   字符串
    •   元组

    可变数据类型的定义:

        可变数据类型,允许变量的值发生变化,即如果对变量进行append、+=等这种操作后,只是改变了变量的值,而不会新建一个对象,变量引用的对象的地址也不会变化,不过对于相同的值的不同对象,在内存中则会存在不同的对象,即每个对象都有自己的地址, 相当于内存中对于同值的对象保存了多份,这里不存在引用计数,是实实在在的对象。

     python中 可变数据类型:

    •   列表
    •   字典

    通过python中数据类型的分类,我们谈论以下几种的拷贝:

    不可变数据类型:

    •     赋值
    •     浅拷贝
    •     深拷贝


    可变数据类型:

    •     赋值
    •     浅拷贝
    •     深拷贝

    不可变数据类型

    1. 赋值

    我们已知python中不可变数据类型:整型、浮点数、字符串、布尔值、元组

    In [1]: a1 = 123    # 整型
    
    In [2]: a2 = a1
    
    In [3]: id(a1), id(a2)
    Out[3]: (1562980880, 1562980880)
    
    In [4]: b1 = 1.123    # 浮点数
    
    In [5]: b2 = b1
    
    In [6]: id(b1), id(b2)
    Out[6]: (1503028953024, 1503028953024)
    
    In [7]: c1 = 'hello'    # 字符串
    
    In [8]: c2 = c1
    
    In [9]: id(c1), id(c2)
    Out[9]: (1503040484272, 1503040484272)
    
    In [10]: d1 = True    # 布尔值
    
    In [11]: d2 = d1
    
    In [12]: id(d1), id(d2)
    Out[12]: (1562722720, 1562722720)
    
    In [13]: e1 = (1, 2, 3, 'hkey')    # 元组
    
    In [14]: e2 = e1
    
    In [15]: id(e1), id(e2)
    Out[15]: (1503040349032, 1503040349032)

    通过以上的例子,a1、a2 赋值的值是一样的。因为python有一个重用机制,对于 不可变数据类型 来说,python并不会开辟一块新的内存空间,而是维护同一块内存地址,只是将 不可变数据类型 对应的地址引用赋值给变量a1、a2。所以根据输出结果,a1和a2其实对应的是同一块内存地址,只是两个不同的引用。

    结论:对于 不可变数据类型 通过'='赋值,不可变数据类型在内存当中用的都是同一块地址。

    2. 浅拷贝

    In [1]: import copy
    
    In [2]: a1 = 3.14
    
    In [3]: a2 = copy.copy(a1)
    
    In [4]: id(a1), id(a2)
    Out[4]: (1690132070168, 1690132070168)

    通过使用copy模块里的copy()函数来进行浅拷贝,把a1拷贝一份赋值给a2,查看输出结果发现,a1和a2的内存地址还是一样。

    结论:对于 不可变数据类型 通过浅拷贝,不可变数据类型在内存当中用的都是同一块地址。

    3. 深拷贝

    In [1]: import copy
    
    In [2]: a1 = 'hello'
    
    In [3]: a2 = copy.deepcopy(a1)
    
    In [4]: id(a1), id(a2)
    Out[4]: (1645307287064, 1645307287064)

    通过使用copy模块里的deepcopy()函数来进行深拷贝,把a1拷贝一份赋值给a2,查看输出结果发现,a1和a2的内存地址还是一样。

    结论:对于 不可变数据类型 通过深拷贝,不可变数据类型在内存当中用的都是同一块地址。

    这里可以对 不可变数据类型 下一个定义了:
        对于python中 不可变数据类型 的赋值、浅拷贝、深拷贝在内存中都是指向同一内存地址。如下图:

    可变数据类型

    1. 赋值

    In [1]: n1 = {"k1": "wu", "k2": 123, "k3": ["alex", 678]}
    
    In [2]: n2 = n1
    
    In [3]: id(n1), id(n2)
    Out[3]: (1888346752712, 1888346752712)

    在上面的例子中,我们使用了列表嵌套列表的方式,赋值后内存空间地址是一致的。其中原理如下图:

    结论:对于 可变数据类型 进行赋值内存地址是不会变化的。

    2. 浅拷贝

    import copy
    n1 = {"k1": "wu", "k2": 123, "k3": ["alex", 678]}
    n3 = copy.copy(n1)  # 浅拷贝
    print("第一层字典的内存地址:")
    print(id(n1))
    print(id(n3))
    print("第二层嵌套的列表的内存地址:")
    print(id(n1["k3"]))
    print(id(n3["k3"]))
    
    
    执行结果:
    
    第一层字典的内存地址:
    2260623665800
    2260625794440
    第二层嵌套的列表的内存地址:
    2260626131144
    2260626131144

    通过以上结果可以看出,进行浅拷贝时,我们的字典第一层n1和n3指向的内存地址已经改变了,但是对于第二层里的列表并没有拷贝,它的内存地址还是一样的。原理如下图:

    结论:对于 python 可变数据类型,浅拷贝只能拷贝第一层地址。

    3. 深拷贝

    import copy
    
    n1 = {"k1": "wu", "k2": 123, "k3": ["alex", 678]}
    n4 = copy.deepcopy(n1)  # 深拷贝
    print("第一层字典的内存地址:")
    print(id(n1))
    print(id(n4))
    print("第二层嵌套的列表的内存地址:")
    print(id(n1["k3"]))
    print(id(n4["k3"]))
    
    执行结果:
    
    第一层字典的内存地址:
    2018569398920
    2018574527176
    第二层嵌套的列表的内存地址:
    2018576058568
    2018576056840

    通过以上结果发现,进行深拷贝时,字典里面的第一层和里面嵌套的地址都已经变了。对于深拷贝,它会拷贝多层,将第二层的列表也拷贝一份,
    如果还有第三层嵌套,那么第三层的也会拷贝,但是对于里面的最小元素,比如数字和字符串,这里就是“wu”,123,“alex”,678之类的,
    按照python的机制,它们会共同指向同一个位置,它的内存地址是不会变的。原理如下图:

    结论:对于 python 可变数据类型,它里面嵌套多少层,就会拷贝多少层出来,但是最底层的数字和字符串不变。

    python 深浅拷贝的实例:

    我们在维护服务器信息的时候,经常会要更新服务器信息,这时我们重新一个一个添加是比较麻烦的,我们可以把原数据类型拷贝一份,在它的基础上做修改。

    实例1:使用浅拷贝

    import copy
    
    # 定义了一个字典,存储服务器信息
    dic = {
        'cpu':[80, ],
        'mem':[80, ],
        'disk':[80, ]
    }
    
    print('before', dic)
    new_dic = copy.copy(dic)
    new_dic['cpu'][0] = 50 # 更新 cpu 为 50
    print(dic)
    print(new_dic)
    
    
    执行结果:
    
    before {'cpu': [80], 'mem': [80], 'disk': [80]}
    {'cpu': [50], 'mem': [80], 'disk': [80]}
    {'cpu': [50], 'mem': [80], 'disk': [80]}

    这时我们会发现,使用浅拷贝时,我们修改新的字典的值之后,原来的字典里面的cpu值也被修改了,这并不是我们希望看到的。

    实例2:使用深拷贝

    import copy
    
    # 定义了一个字典,存储服务器信息
    dic = {
        'cpu':[80, ],
        'mem':[80, ],
        'disk':[80, ]
    }
    
    print('before', dic)
    new_dic = copy.deepcopy(dic)
    new_dic['cpu'][0] = 50 # 更新 cpu 为 50
    print(dic)
    print(new_dic)
    
    
    执行结果:
    
    before {'cpu': [80], 'disk': [80], 'mem': [80]}
    {'cpu': [80], 'disk': [80], 'mem': [80]}
    {'cpu': [50], 'disk': [80], 'mem': [80]}

    使用深拷贝的时候,发现只有新的字典的cpu值被修改了,原来的字典里面的cpu值没有变。大功告成!

  • 相关阅读:
    20200924-3 单元测试,结对
    20200924-1 每周例行报告
    20200924-5 四则运算试题生成,结对
    20200924-2 功能测试
    20200924-4 代码规范,结对要求
    20200929-git地址
    20200917-1 每周例行报告
    20200917-2 词频统计
    20200917-3 白名单
    20200910-2 博客作业
  • 原文地址:https://www.cnblogs.com/hukey/p/9812035.html
Copyright © 2020-2023  润新知