Python中的浅复制(shallow copy)和深复制(deep copy)

版权声明:本文为TensorSense原创文章, 转载请注明出处, 转载请注明出处 ! https://blog.csdn.net/u011995719/article/details/82911392


近期杂事太多,博客一直没更新,9月最后一天了,总得写点吧

今天记一下以前碰到过,最近又碰到的问题:python的深复制和浅复制
神奇的python中,copy竟然还有两种,一深一浅(emm),来看看它们有什么区别。

python值管理方式

这本质原因是python采用的是基于值的管理方式,
而C语言中,系统会为每个变量分配内存空间,当改变变量的值时,改变的是内存空间中的值,变量的地址是不改变的。
基于值的管理方式是什么意思呢? 来看一个例子:

a = [1,2]
b = a
print ('a: ', a, 'id(a): ', id(a))
print ('b: ', b, 'id(a): ', id(b))

输出:
('a: ', [1, 2], 'id(a): ', 4496519824)
('b: ', [1, 2], 'id(a): ', 4496519824)

可以发现a和b的内存地址相同,也就是说,a和b是完全相同的,b只是a的一个引用(也就是一个别名)
例如,帝都=北京,帝都就指的是北京这座城市,是完全相同的物理存在。这就是python的基于值的管理。

当我们采用“=”对参数进行赋值的时候,其实就是一个引用,若对引用进行了修改,原始参数也将被改变,这是我们在开发过程中很容易忽略的,也会导致我们的数据被意外修改,导致奇怪的bug。
请看例子:

a = [1,2]
b = a
b.append(3)
print ('a: ', a, 'id(a): ', id(a))
print ('b: ', b, 'id(a): ', id(b))

输出:
('a: ', [1, 2, 3], 'id(a): ', 4335018640)
('b: ', [1, 2, 3], 'id(a): ', 4335018640)

可以看到,对b进行了增加一个元素,同时地,a也被修改了。

通过以上两个小例子可以了解python值的管理方式,以及知道"="赋值的本质。
那么当我们想复制出一个完全独立于原始变量的变量,应该怎么做呢?
这就需要浅复制(shallow copy) 和 深复制(deep copy)。

深复制与浅复制的使用及区别

在模块copy中有deepcopy方法用于深复制,copy方法用于浅复制。
深复制和浅复制到区别,在于复制的对象中是否有可变类型,aha?,可变类型是什么?
简单复习一下,python中

可变类型有:列表,字典
不可变类型有:数字,字符串,元组

为了循序渐进,这里分两个例子讲解

例子1:

a = [1, [2, 3]]
a_equal = a
a_shallowcopy = copy.copy(a)
a_deepcopy = copy.deepcopy(a)
a[0] = 4    # 改变a中的第一个元素,即改变a中的数字
print'a:               ', a,       'id(a):             ', id(a)
print'a_equal:         ', a_equal, 'id(a_equal):       ', id(a_equal)
print'a_shallowcopy:   ', a_shallowcopy, 'id(a_shallowcopy): ', id(a_shallowcopy)
print'a_deepcopy:      ', a_deepcopy, 'id(a_deepcopy):    ', id(a_deepcopy)

输出:
a: [4, [2, 3]] id(a): 4578314360
a_equal: [4, [2, 3]] id(a_equal): 4578314360
a_shallowcopy: [1, [2, 3]] id(a_shallowcopy): 4562436248
a_deepcopy: [1, [2, 3]] id(a_deepcopy): 4562362096

可以发现:
=赋值: 对于修改,受到影响,地址相同
浅复制:对于修改,不受影响,地址不相同
深复制:对于修改,不受影响,地址不相同

这么一看,深复制和浅复制完全一样啊,哪里有区别?
还记得上面说的可变类型吗? 刚一看刚刚改变list a当中的第一个元素,是一个数字吧。
数字是不可变类型,所以我们在修改不可变类型时,深复制和浅复制没有区别。
那么当我们改变a当中第二个元素,即list呢,会怎么样,请看例子2:

a = [1, [2, 3]]
a_equal = a
a_shallowcopy = copy.copy(a)
a_deepcopy = copy.deepcopy(a)
a[0] = 4    # 改变a中的第一个元素,即改变a中的数字
a[1][1] = 5     # 改变a当中第二个元素中的元素,即 改变a当中的list
print'a:               ', a,       'id(a):             ', id(a)
print'a_equal:         ', a_equal, 'id(a_equal):       ', id(a_equal)
print'a_shallowcopy:   ', a_shallowcopy, 'id(a_shallowcopy): ', id(a_shallowcopy)
print'a_deepcopy:      ', a_deepcopy, 'id(a_deepcopy):    ', id(a_deepcopy)

输出:
a: [4, [2, 5]] id(a): 4454672504
a_equal: [4, [2, 5]] id(a_equal): 4454672504
a_shallowcopy: [1, [2, 5]] id(a_shallowcopy): 4438794392
a_deepcopy: [1, [2, 3]] id(a_deepcopy): 4438720240

可以发现:
=赋值: 对于修改,受到影响,地址相同
浅复制:对于不变类型的修改,不受影响;对于可变类型的修改,受影响,地址不相同
深复制:对于不变类型的修改,不受影响;对于可变类型的修改,不受影响,地址不相同

为了不必要的麻烦,我们想要一个完全独立的变量,要一个与原始变量解耦的变量,那请使用深复制吧。

还可以从另外一个角度理解深复制和浅复制的区别,那么就是
浅复制仅复制父对象,不会复制对象内部的子对象(如例子2当中的a的第二个元素[2,3])
深复制不仅复制父对象,而且还复制内部的子对象。

什么是子对象呢? 那就是可变类型,所以还是回到了开头提到的,深复制和浅复制的区别,就要看变量中是否含有可变对象,哈哈。

(转载请注明出处:https://blog.csdn.net/u011995719/article/details/82911392 by TensorSense)

猜你喜欢

转载自blog.csdn.net/u011995719/article/details/82911392