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并没有拷贝这个对象,而是拷贝了这个对象的引用。
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对象等)没有拷贝一说,浅拷贝是用完全切片操作来完成的。
第二:如果元组变量只包含原子类型对象,对他的拷贝将不会进行,即使使用深拷贝操作也只能得到一个浅拷贝。