Python中对象的引用与复制
在python进行像b = a这样的赋值时,只会创建一个对a的新引用,使a的引用计数加1,而不会创建新的对象:
>>> a = 'xyz' >>> import sys >>> sys.getrefcount(a) 3 >>> b = a >>> sys.getrefcount(b) 4 >>> id(a) 88292288L >>> id(b) 88292288L
这样,当引用的对象是可变对象的时候(列表,字典,可变集合等),会产生意料之外的行为:
>>> a = [1, 2, 3, 4] >>> b = a >>> b.append(5) >>> a [1, 2, 3, 4, 5]
因为a和b引用的是同一对象,改变其中一个,另外一个也会随之改变。当我们想建立一个副本而不是引用时,可以复制对象。
复制对象一般使用copy模块:
>>> a = [1, 2, 3, 4] >>> import copy >>> b = copy.copy(a) >>> b.append(5) >>> b [1, 2, 3, 4, 5] >>> a [1, 2, 3, 4]
这样就可以了,但这种复制是一种浅复制,复制的新对象中包含的是对原始对象中的项的引用,如果对象的项为可变对象,也会产生不可控行为:
>>> a = [1, [1, 2]] >>> b = copy.copy(a) >>> b[1].append(3) >>> b [1, [1, 2, 3]] >>> a [1, [1, 2, 3]]
这时候就要使用深复制了。深复制将创建一个新对象,并递归地复制它所包含的所有对象:
>>> a = [1, [1, 2]] >>> b = copy.deepcopy(a) >>> b[1].append(3) >>> b [1, [1, 2, 3]] >>> a [1, [1, 2]]
对于不可改变的对象而言(字符串,数字,元组)等,没有必要拷贝,因为它们是不可改变的,不用担心会不经意间改动了它们。拷贝操作也只会得到原对象:
>>> a = (1, 2, 3) >>> b = copy.copy(a) >>> a is b True
对于可变对象来(列表,字典,可变集合)来说,可以分别使用内置函数list(),dict(),set()来进行浅复制,速度是比使用copy模块快的。
列表也可以使用切片进行浅复制:
>>> a = [1, 2, 3, 4] >>> b = a[:] >>> a is b False >>> b [1, 2, 3, 4]
对序列数据类型(字符串,列表,元组)进行*操作时,也仅仅是复制了对象中项的引用,如果使用*创建一个多维列表:
>>> a = [1, 2, 3] >>> b = [a] >>> c = b * 3 >>> a.append(4) >>> c [[1, 2, 3, 4], [1, 2, 3, 4], [1, 2, 3, 4]]
最好是在列表推导中使用浅复制来创建多维列表,可以避免隐式的引用共享:
>>> a = [1, 2, 3] >>> c = [list(a) for i in range(3)] >>> a.append(4) >>> c [[1, 2, 3], [1, 2, 3], [1, 2, 3]]