python中的浅拷贝和深拷贝

时间:2022-04-13 22:28:19

1、赋值语句

1 a = 'abc'
2 b = a
3 print id(a)
4 print id(b)
5 
6 # id(a):29283464
7 # id(b):29283464

通过简单的复制,我们可以看到,a b其实是一个对象。

对象赋值实际上是简单的对象引用,也就是说,当你创建了一个对象,然后把它赋值给另一个变量时,python并没有拷贝这个对象,而是拷贝了这个对象的引用。

python中的浅拷贝和深拷贝

2、浅拷贝

序列类型的对象默认类型拷贝是浅拷贝,通过以下几种方式实施:

  (1) 完全切片操作[:]

  (2) 利用工厂函数,例如list() 、dict()等

  (3) 使用copy模块中的copy函数

创建一个列表对象,然后分别用切片操作和工厂方法拷贝对象,然后使用id()内建函数来显示每个对象的标识符。

s = ['abc',['def',1]]
a = s[:]
b= list(s)
print [id(x) for x in s,a,b]
#[29262792.29238576,28957272]

 

可以看到创建了三个不同的列表对象。在对对象的每一个元素进行操作:

a[0] = 'a'
b[0] = 'b'
print a,b
# (['a',['def',1]],['b',['def',1]])

a[1][1] = 0
print a,b
# (['a',['def',0]],['b',['def',0]])

我们可以看到,当执行a[1][1] = 0时,b[1][1]也跟着变为0。这是因为我们仅仅做了一个浅拷贝,对一个对象进行浅拷贝其实是新创建了一个类型跟原对象一样,它的内容元素是原来对象元素的引用,换句话说,这个拷贝的对象是新的,但他的内容还是原来的。这就是浅拷贝。

但是我们看到a的第一个元素,即字符串被赋值后,并没有影响b的。这是因为在这个对象中,第一个字符串类型是不可变的,第二个列表对象是可变的。正因为如此,当进行浅拷贝时,字符串被显式的拷贝,并创建了一个新的字符串对象,而列表元素只是把它的引用复制了,并不是他的成员。

#改变前
print [id(x) for x in a]
#[29563284,27843874]
print [id(x) for x in b]
#[29563284,27843874]

#改变后
print [id(x) for x in a]
#[23464734,27843874]
print [id(x) for x in b]
#[29884656,27843874]

 

3 深拷贝

根据上面的例子,如果我们想要在改变a时不影响到b,要得到一个完全拷贝或者说深拷贝(一个新的容器对象,包含原有对象元素全新拷贝的引用),就需要copy.deepcopy()函数。

import copy
s = ['abc',['def',1]]
a = copy.deepcopy(s)
b = copy.deepcopy(s)
print [id(x) for x in s,a,b]
#[29278336,29262792,29238576]

a[0] = 'a' b[0] = 'b' a[1][1] = 0 print a,b #(['a',['def',0]],['b',['def',1]])

需要注意的是:第一,非容器类型(比如数字、字符串和其他“原子”类型的对象,像代码、类型和xrange对象等)没有拷贝一说,浅拷贝是用完全切片操作来完成的。

       第二:如果元组变量只包含原子类型对象,对他的拷贝将不会进行,即使使用深拷贝操作也只能得到一个浅拷贝。