• 深拷贝-浅拷贝


    深拷贝浅拷贝是个很容易迷糊的问题,本人帮你彻底搞清楚。

    粗识内存

    本人没学过c,内存略懂,有堆栈之分,

    栈可以理解为程序自动分配的内存,堆可以理解为程序员对内存的引用,不重要,有感觉就行。

    浅拷贝

    浅拷贝并不是我们认知中的“复制”,浅拷贝只是对象的引用,是对一个对象的浅层拷贝,所以叫浅拷贝

    或者说是顶层拷贝,只拷贝了引用,没拷贝内容

    示例代码

    import copy
    
    a = [1, 2, [3, 4]]
    b = copy.copy(a)
    
    print('b:', b)      # ('b:', [1, 2, [3, 4]])
    
    b.append(5)
    b[2].append(6)
    
    print('a:', a)      # ('a:', [1, 2, [3, 4, 6]])
    print('b:', b)      # ('b:', [1, 2, [3, 4, 6], 5])

    为什么这样呢?

    b是a的浅拷贝,对b的操作不会影响a,但是对b内元素操作会影响a,因为ab指向的是同一个元素

    b只拷贝了a的第一层,所以叫浅拷贝

    print(id(a))        # 40641880
    print(id(b))        # 40667616      b新建了一个对象
    print(id(a[2]))     # 40643080
    print(id(b[2]))     # 40643080      b中元素仍是a中元素

    深拷贝

    深拷贝才是我们认知中的“复制”,深拷贝是完全拷贝,是对一个对象的深层拷贝,所以叫深拷贝。

    示例代码

    import copy
    
    a = [1, 2, [3, 4]]
    b = copy.deepcopy(a)
    
    print('b:', b)      # ('b:', [1, 2, [3, 4]])
    
    b.append(5)
    b[2].append(6)
    
    print('a:', a)      # ('a:', [1, 2, [3, 4]])
    print('b:', b)      # ('b:', [1, 2, [3, 4, 6], 5])

    好理解了

    互不影响

    b拷贝了a的所有层,所以叫深拷贝

    print(id(a))        # 41624920
    print(id(b))        # 41648496      b新建了一个对象
    print(id(a[2]))     # 41626120
    print(id(b[2]))     # 41648456      b中元素不是a中元素

    可以看到深拷贝和浅拷贝都新建了一个对象。

    等于

    那等于是什么?深拷贝?浅拷贝?都不是,等于才是真正的复制。

    示例代码

    a = [1, 2, [3, 4]]
    b = a
    
    print('b:', b)      # ('b:', [1, 2, [3, 4]])
    
    b.append(5)
    b[2].append(6)
    
    print('a:', a)      # ('a:', [1, 2, [3, 4, 6], 5])
    print('b:', b)      # ('b:', [1, 2, [3, 4, 6], 5])
    
    
    print(id(a))        # 40510648
    print(id(b))        # 40510648      b没有新建对象
    print(id(a[2]))     # 40490568
    print(id(b[2]))     # 40490568      b中元素仍是a中元素

    互相影响,因为是同一个对象

     所以  复制 != 拷贝

    好了,终于明白了,真的吗?

    看个实例

    jack = ['jack', ['age', 20]]
    tom = jack[:]
    anny = list(jack)
    print id(jack), id(tom), id(anny)    # 144846988 144977164 144977388

    3个不同的对象

    tom[0] = 'tom'
    anny[0] = 'anny'
    print jack, tom, anny   # ['jack', ['age', 20]] ['tom', ['age', 20]] ['anny', ['age', 20]]

    可以理解

    anny[1][1] = 18
    print jack, tom, anny   # ['jack', ['age', 18]] ['tom', ['age', 18]] ['anny', ['age', 18]]

    请问,还理解吗?

    分析:上面是新建了3个对象,那么只能是深拷贝或浅拷贝中的一种,改了名字互不影响,应该是深拷贝,但是改了年龄互相影响,又不是深拷贝,那是浅拷贝?也不对啊,迷糊了

    [id(x) for x in jack]  # [3073896320L, 3073777580L]
    [id(x) for x in tom]   # [144870744, 3073777580L]
    [id(x) for x in anny]  # [144977344, 3073777580L]

    可以看到名字id不同,年龄id相同

    为什么会这样呢?

    因为python中字符串是不可变类型,其被修改时,必须新建一个对象,这就明白了,就是浅拷贝,年龄指向了同一个id,但是因为名字修改时重新创建了对象, 所以指向了不同的id

    那我们在上面浅拷贝的例子上验证下

    b[2] = 3
    print('a:', a)      # ('a:', [1, 2, [3, 4, 6]])
    print('b:', b)      # ('b:', [1, 2, 3, 5])

    果然改变了b[2],新建了个对象,a[2]并没有改变

    如果用深拷贝就没有上面的问题

    jack = ['jack', ['age', '20']]
    import copy
    tom = copy.deepcopy(jack)
    anny = copy.deepcopy(jack)
    # 根据第一个思路进行重命名,重定岁数操作:
    tom[0] = 'tom'
    anny[0] = 'anny'
    print jack, tom, anny  # ['jack', ['age', '20']] ['tom', ['age', '20']] ['anny', ['age', '20']]
    anny[1][1] = 18
    print jack, tom, anny  # ['jack', ['age', '20']] ['tom', ['age', '20']] ['anny', ['age', 18]]

    总结

    切片操作和工厂方法list方法拷贝是浅拷贝,而且一般情况下都是浅拷贝

    深拷贝浅拷贝需要考虑数据是否是可变类型

  • 相关阅读:
    Kubernetes进阶实战读书笔记:持久化存储卷(pvc详解)
    Nginx核心知识100讲学习笔记(陶辉)详解HTTP模块(接受请求模块|正则表达式|冲突合并)
    Nginx核心知识100讲学习笔记(陶辉)详解HTTP模块(详解11阶段)
    WIndows 编辑的文件的文件在Linux下查看每行末尾有 ^M
    JQuery ajax 使用
    查看oracle死锁进程并结束死锁
    Oracle 死锁处理(查看锁表语句及解锁)
    BS表单编程注意事项
    Delphi MDI多文档架构几个问题解决
    Delphi MDI子窗体
  • 原文地址:https://www.cnblogs.com/yanshw/p/10694941.html
Copyright © 2020-2023  润新知