python深拷贝与浅拷贝

2019-03-10  本文已影响0人  gregocean

一样是一篇个人梳理,因为这个东西被无数人研究过了
推荐文章:
图解Python深拷贝和浅拷贝
另外这篇的深拷贝部分也有点意思。
你真得理解 python 的浅拷贝和深拷贝吗?

下面大部分内容是基于另外一篇博客,源不可考。

直接赋值

首先,如果我们不进行拷贝,而是直接赋值,很有可能会出现意料之外的结果。比如a是一个列表,b=a,那么修改a的同时,b也会同样被修改,因为Python对象的赋值都是进行引用(内存地址)传递的,实际上a和b指向的都是同一个对象。

>>> a = [1,2,3]
>>> b = a
>>> a[2] = 4
>>> a
[1, 2, 4]
>>> b
[1, 2, 4]
>>> a is b
True

浅拷贝

为了避免这种情况发生,我们可以使用浅拷贝:

>>> a = [1,2,3]
>>> import copy
>>> b = copy.copy(a)
>>> b is a
False
>>> a[0] is b[0]
True

浅拷贝会创建一个新的对象,然后把生成的新对象赋值给新变量。注意这句话的意思,上面这个例子中1,2,3这三个int型对象并没有创建新的,新的对象是指copy创建了一个新的列表对象,这样a和b这两个变量指向的列表对象就不是同一个,但和两个列表对象里面的元素依然是按引用传递的,所以a列表中的对象1和b列表中的对象1是同一个。 但是这时修改a列表的不可变对象,b列表不会受到影响:

>>> a[0] = 4
>>> a
[4, 2, 3]
>>> b
[1, 2, 3]

由于浅拷贝时,对于对象中的元素,浅拷贝只会使用原始元素的引用(内存地址),所以如果对象中的元素是可变对象,浅拷贝就没辙了。比方说列表中包含一个列表,这时改动a,浅拷贝的b依然可能受影响:

>>> a = [1,2,[3,]]
>>> b = copy.copy(a)
>>> a is b
False
>>> a[2].append(4)
>>> a
[1, 2, [3, 4]]
>>> b
[1, 2, [3, 4]]

可以产生浅拷贝的操作有以下几种:

工厂函数看上去像函数,实质上是类,调用时实际上是生成了该类型的一个实例,就像工厂生产货物一样.

深拷贝

对于这个问题,又引入了深拷贝机制,这时不仅创建了新的对象,连对象中的元素都是新的,深拷贝都会重新生成一份,而不是简单的使用原始元素的引用(内存地址)。注意了,对象中的元素,不可变对象还是使用引用,因为没有重新生成的必要,变量改动时会自动生成另一个不可变对象,然后改变引用的地址。但可变对象的内容是可变的,改动后不会产生新的对象,也不会改变引用地址,所以需要重新生成。

>>> a = [1,2,[3,]]
>>> b = copy.deepcopy(a)
>>> a is b
False
>>> a[0] is b[0]
True
>>> a[2] is b[2]
False

这时再改变a中的元素对b就完全没有影响了:

[1, 2, [3]]
>>> a = [1,2,[3,]]
>>> b = copy.deepcopy(a)
>>> a[2].append(4)
>>> a
[1, 2, [3, 4]]
>>> b
[1, 2, [3]]

特殊情况

>>> a = 'hello'
>>> b = copy.copy(a)
>>> c = copy.deepcopy(a)
>>> a is b
True
>>> a is c
True

对于这种类型的对象,无论是浅拷贝还是深拷贝都不会创建新的对象。

原子类型指所有的数值类型以及字符串

>>> a=(1,2,3)
>>> a = (1,2,3)
>>> b = copy.copy(a)
>>> c = copy.deepcopy(a)
>>> a is b
True
>>> a is c
True

元组本身是不可变对象,如果元组里的元素也是不可变对象,就没有进行拷贝的必要了。实测如果元组里面的元素是只包含原子类型对象的元组,则也属于这个范畴。

>>> a = (1,2,(3,))
>>> b = copy.copy(a)
>>> c = copy.deepcopy(a)
>>> a is c
True
>>> a is b
True

总结

基于以上讨论,我们可以轻松理解下面的现象。

函数传参

a=1
def func(a):
    a=2
func(a)
print a  # 1
a=[]
def func(a):
    a.append(1)
func(a)
print a  # [1]

字典浅拷贝

a = []
for i in xrange(5):
    a.append({'num': i})
print a  # [{'num':0},...,{'num':4}]
a = []
d = {'num': 0}
for i in xrange(5):
    d['num'] = i
    a.append(d)
print a  # [{'num':4},...,{'num':4}]
上一篇 下一篇

猜你喜欢

热点阅读