Python中的浅拷贝和深拷贝

如果要将一个数据赋给另一个数据要怎么做呢?最直观的一种方法是使用 =,另一种方法使用copy(),另一种是使用copy库中的deepcopy()。那么这三种方法有什么区别呢?下面我们通过图示的方法来看一下。

1. Python中的浅拷贝

1.1 = 赋值

假设定义一个三维数组a = [1,2,3, [1,2,3, [5,6,7]]],如果使用=将其赋给另一个变量b,那么a和b会有什么不同呢?
Python中的浅拷贝和深拷贝

从上图可以看出,a和b指向的是同样的地址,而且通过id()来获取内存空间的地址进行比较也验证了我们的想法。这样就导致一个问题,当a或b中有一个对数组元素进行了改变,另一个数组的元素也会随之发生改变。这是因为=传递的是对象的地址,当使用它急性拷贝时,实际上就是让两个变量指向了同一块地址空间,因此它们拥有的东西自然就是相同的了。

1.2 copy()

如果使用copy()进行拷贝,原变量和拷贝得到的变量会有什么区别呢?
Python中的浅拷贝和深拷贝

从上图可以看到,数组最外层的东西在内存空间进行了复制,因此它们指向的是不同的内存空间。这从id()的结果也可以看出,它们的内存空间地址是不同。但是除去最外层之外,其他的东西仍然是相同的,它们拥有的东西是一样的。因此,当两个变量改变最外层的元素时,它们两个是互不影响的;而当它们改变内层的元素时,其中一个改变会直接影响另一个。
Python中的浅拷贝和深拷贝

这是因为使用copy()的浅拷贝是对一个对象父级(外层)的拷贝,并不会拷贝子级(内部)。对于外层数据来说又有两种情况:

  • 如果最外层的数据类型是可变的,比如说列表,字典等,浅拷贝会开启新的地址空间去存放
  • 如果最外层的数据类型是不可变的,比如元组,字符串等,浅拷贝对象的时候,还是引用对象的地址空间。这是因为,不可变对象无法更改,在复制一份数据的意义不大

2. Python中的深拷贝

Python中的深拷贝需要依赖于copy库中的deepcopy(),那么它和浅拷贝的结果有什么不同呢?
Python中的浅拷贝和深拷贝

从上图中可以看出,深拷贝对于不同层次上的数据都进行了复制,它得到的结果从外到内和之前的变量指向的东西是完全不同的。因此其中一个任意位置元素的更新都不会影响另一个,因此它们所拥有的是完全不同的两个东西。
Python中的浅拷贝和深拷贝

同样深拷贝也分两种情况:

  • 最外层数据类型可变:此时内部和外部数据都会进行拷贝
  • 最外层数据类型不可变:
    • 如果里面是可变数据类型,会新开辟地址空间存放,如上所示
    • 如果内部数据类型不可变,如同浅拷贝一样进行引用地址的传递

3. 参考

python之浅拷贝和深拷贝的区别

十分钟!彻底弄懂Python深拷贝与浅拷贝机制