zoukankan      html  css  js  c++  java
  • 深拷贝-浅拷贝

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

    粗识内存

    本人没学过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方法拷贝是浅拷贝,而且一般情况下都是浅拷贝

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

  • 相关阅读:
    170619、springboot编程之HelloWorld
    170616、解决 java.lang.IllegalArgumentException: No converter found for return value of type: class java.util.ArrayList
    170615、spring不同数据库数据源动态切换
    pytest文档10-命令行传参
    pytest文档9-参数化parametrize
    pytest文档8-html报告报错截图+失败重跑
    pytest文档7-pytest-html生成html报告
    定位对应关系
    ADB 无线连接
    command failed shell "ps 'uiautomator'"的解决方式
  • 原文地址:https://www.cnblogs.com/yanshw/p/10694941.html
Copyright © 2011-2022 走看看