Python中的深浅复制

引子

最近遇到了一个问题,在python中对于对象的复制有两种,copy()以及deepcopy(),为了研究他们之间的区别,写下以下博客。
首先查看python的官方文档,获得以下解释:python

浅层复制和深层复制之间的区别仅与复合对象 (即包含其余对象的对象,如列表或类的实例) 相关:
一个 浅层复制 会构造一个新的复合对象,而后(在可能的范围内)将原对象中找到的 引用 插入其中。
一个 深层复制 会构造一个新的复合对象,而后递归地将原始对象中所找到的对象的 副本 插入。

为了明白其中的原理,作了以下实验:code

简单对象的复制

简单对象是指不包含子对象的对象,也就是只包含普通元素(数字,字符串)的对象,对于简单对象,copy与deepcopy方法没有什么区别,下面的一段代码,能够先看一下效果是怎样的。对象

import copy

if __name__ == '__main__':
    a = [1, 2, 3, 4]
    b = copy.copy(a)
    c = copy.deepcopy(a)  
    print(a == b)
    print(a is b)
    print(a == c)
    print(a is c)

执行上面的代码,咱们能够看到执行结果,深复制和浅复制的执行结果是同样的递归

True # 说明 a 和 b 所指向的对象的内容相同
False # 说明 a 和 b 所指向的不是同一个对象(地址不一样)
True # 说明 a 和 c 所指向的对象的内容相同
False # 说明 a 和 c 所指向的不是同一个对象(地址不一样)

能够用一张图来解释一下,为何简单对象的深浅复制是同样的。内存

image-20210116191553273
咱们知道,上面的图中,变量a指向一个List对象(或者说是一个List对象的引用),该对象在内存中占用一个地址空间,当简单对象执行copy和deepcopy中的对象时,咱们能够看到不管时深复制仍是浅复制,都是在内存中新开辟一个地址空间,将原来对象中的内容复制过去,同时让b成为新对象的引用。所以,咱们看到a和b指向的对象是不一致的,可是内容是相同的。文档

复杂对象的复制

复杂对象能够理解为另外包含其余简单对象的对象,也就是包含子对象的对象,例如:List中嵌套List,或者Dict中嵌套List等,对于复杂对象咱们先来看一个简单的程序示例。字符串

import copy

if __name__ == '__main__':
    a = {'name': 'test', 'age': 56, 'address': [1, 2, 3, 4, 5]}
    b = copy.copy(a)
    print(a is b)
    print(a['address'] is b['address'])
    c = copy.deepcopy(a)
    print(a is c)
    print(a['address'] is c['address'])

执行结果以下:博客

False  # 说明 a 和 b 不是同一个对象的引用
True   # 说明 a中的address 和 b 中的 address 是同一个对象。黑人问号脸??
False  # 说明 a 和 c 不是同一个对象的引用
False  # 说明 a中的address 和 c 中的 address 不是同一个对象

下面我经过一张图,来大概解释一下为何会出现上面的结果。PS:具体对象的对象不必定是按照图中的方式,为了可以说明原理,本图中将子对象的存储空间单独抽出,方便理解。class

image-20210116194842905
咱们看到对于复杂对象(包含子对象的对象)的复制,深浅复制在实现原理上就有所不一样了。经过上图咱们能够看到,复杂对象的深浅复制的区别在于复杂对象的子对象。能够看到:test

总结

  • 对于简单对象,不管是深复制仍是浅复制,咱们能够看到,Python都是采用的直接在内存中开辟新的地址空间,而后将值复制到新的地址空间。
  • 对于复杂对象,复杂对象的简单对象部分,按常规的简单对象进行处理,对于复杂对象的子对象部分来讲:深复制是在内存中开辟一个新的空间,而且将子对象复制到新的地址空间,可是对于浅复制而言,咱们能够看到并无对子对象来开辟空间,经过图看到,新复制的对象和原来的对象同时指向了同一个List对象(也就是同一个对象的引用),因此咱们看到a[‘address’]和b[‘address’]同时指向同一个对象。
相关文章
相关标签/搜索