python深拷贝和浅拷贝之简单分析

发布时间:2019-03-04 15:58:43编辑:auto阅读(2043)


    title: python 深拷贝和浅拷贝
    tags: python,copy,deepcopy
    grammar_cjkRuby: true
    ---

    python 深拷贝和浅拷贝

    python的变量的赋值都是引用
    把一个变量赋值给一个变量,不是拷贝这个对象,而是拷贝这个变量的引用

    1. 直接赋值

      传递的是这个变量的引用

    2. 浅拷贝

      拷贝的是这个变量的引用,会产生新的对象
      浅拷贝会产生一个新的对象,但是浅拷贝的内容还是原有对象的引用
      看下面的例子

    浅拷贝
    import copy
    a = [1, 2, 3, 4 ,5]
    b = copy.copy(a)
    print id(a)
    140637017675968
    print id(b)
    140637017452416
    print [id(item) for item in a]
    [30363992, 30363968, 30363944, 30363920, 30363896]
    print [id(item) for item in b] 
    [30363992, 30363968, 30363944, 30363920, 30363896]

    我们可以看到,copy.copy这个浅拷贝的操作,产生了一个新的对象
    因此,id(a) ,id(b)是不同的
    但是,浅拷贝对于所拷贝对象中的各个元素,只会使用这些元素的引用,并不会再继续产生新的对象
    所以,两者各个元素的id是相同的

    再看个浅拷贝的例子

    import copy
    a = [1, 2, 3, 4 ,5]
    b = copy.copy(a)
    print id(a)
    140337638905904
    print id(b)
    140337638904824
    a.append(6)
    print a
    [1, 2, 3, 4, 5, 6]
    print id(a)
    140337638905904
    print b
    [1, 2, 3, 4, 5]
    print id(b)
    140337638904824

    可以看到,因为浅拷贝也是创建了一个新的对象,所以a 和 a的浅拷贝b,是两个不同的对象
    所以给对象a添加元素,并不会对b产生影响

    再来看一个浅拷贝的例子 这个浅拷贝的例子对于其拷贝的对象也产生了影响,为什么呢?

    import copy
     a = [1, 2, 3, [1]]
    print id(a)
    140063950885808
    b = copy.copy(a)
    print id(b)
    140063949633512
    print [id(item) for item in a]
    [29266264, 29266240, 29266216, 140063949688416]
    print [id(item) for item in b]
    [29266264, 29266240, 29266216, 140063949688416]
    a[3].append(2)
    print a
    [1, 2, 3, [1, 2]]
     print b
    [1, 2, 3, [1, 2]]

    可以看到,这个例子中,a对象和其拷贝产生的对象b
    首先,浅拷贝,新建了一个对象b
    a 和 b是两个不同的对象
    由于浅拷贝,新建的对象b的各个元素其实是原对象a的各个元素的引用(这是和上一个例子不同的地方,上一个例子是原有对象新增了一个元素)
    这个例子是原有对象对于其最后一个元素(也是列表类型)新增了一个元素
    因为 a 和b 两个对象的各个元素其实是一样的(b的各个元素其实是a的各个元素的引用),所以a 的某个元素的变化会让b一样的变化

    我们再看一个浅拷贝的例子

    import copy
    a = [1, 2, 3, [1]]
    print id(a)
    140063950885808
    b = copy.copy(a)
    print id(b)
    140063949633512
    print [id(item) for item in a]
    [29266264, 29266240, 29266216, 140063949688416]
    print [id(item) for item in b]
    [29266264, 29266240, 29266216, 140063949688416]
    a[3].append(2)
    print a
    [1, 2, 3, [1, 2]]
    print b
    [1, 2, 3, [1, 2]]
    a[0] = 10
    a
    [10, 2, 3, [1, 2]]
    b
    [1, 2, 3, [1, 2]]
    print [id(item) for item in a]
    [29266048, 29266240, 29266216, 140063949688416]
    print [id(item) for item in b]
    [29266264, 29266240, 29266216, 140063949688416]

    前面的操作都没毛病,怎么最后对于对象a的第一个元素的操作让a变化了,却没有影响到b呢?
    这是因为操作的a的第一个元素是int类型,这是不可变对象,对于不可变对象的重新赋值本质上是新建一个新的对象
    所以,其实这样操作后a的第一个元素对应的对象已经不是原来的对象了,当然不会影响到b

    深拷贝
    import copy
    a = [1, 2, 3, 4, [1]]
    b = copy.deepcopy(a)
    print [id(item) for item in a]
    [40378712, 40378688, 40378664, 40378640, 140313123501048]
    print [id(item) for item in b]
    [40378712, 40378688, 40378664, 40378640, 140313122753568]
    a[4].append(2)
    a
    [1, 2, 3, 4, [1, 2]]
    b
    [1, 2, 3, 4, [1]]

    在上面,我们可以看到,深拷贝,对于a 的 a[4] 即一个可变对象(list类型)而言,是产生了一个新的对象,而不是像浅拷贝一样,只是原对象元素的引用
    因此,原对象的变化,不会引起拷贝对象的变化

    总结:
    对于浅拷贝,深拷贝来说,如果拷贝对象的元素是不可变类型(或者说不可变对象)
    则无论浅拷贝,深拷贝,对原有对象的改变,都不会影响到拷贝的对象的这个元素
    因为,不可变类型的更改,其实是新建了一个对象,自然不会影响到原有对象
    当被拷贝对象含有可变类型的元素的时候,对于原有对象的这个可变类型的元素的更改:
    浅拷贝,会影响到被拷贝对象
    深拷贝,不会影响到被拷贝对象,因为深拷贝对于不可变类型的元素,是产生了一个新的对象来复制这个元素的
    所以,如果希望复制一份原有对象,不被影响,需要使用深拷贝

关键字