Python的对象拷贝
对象拷贝
在面向对象编程中,复制一个现有对象的副本,被称为对象拷贝。生成的对象被称之为对象副本。
拷贝是基本的操作,但是在实际操作过程中会有一些需要注意的地方。有多种方法来拷贝对象,最常见的是通过拷贝构造函数或者克隆函数。拷贝的主要目的是对副本做出修改、移动,或者是保留当前值。
如果不需要实现以上目的,则创建对原始数据的引用是更加高效的选择。
Python的对象拷贝
在Python中,赋值语句总是建立对象的引用,看下面的一段代码
>>> a = [1,2,3]
>>> b = a
>>> print(id(a), id(b))
(4420030688, 4420030688)
会发现b = a
操作后,a
和b
指向了同一块地址。
上面例子中,a
和b
都是可变对象(list)的,对b
的修改,同样可会影响到a
,如下
>>> b.append(3)
>>> a
[1, 2, 3, 3]
那么对于可变的数据的副本,或者包含了可变对象的数据的副本,如何在不改变原数据的情况下操作副本呢?这里就要用到内置的copy
模块了。
浅拷贝
浅拷贝在浅拷贝中,会将原对象中所有字段复制给对象副本。如果某个字段是某个对象的引用(比如,一串内存地址),那么被拷贝的是这个引用,指向元数据中该字段指向的对象;如果字段是基本类型,则复制这个字段的值。
在Python这样万物皆是对象的语言中,使用浅拷贝后,对象副本中字段和原始数据中字段都会指向同一个对象,这样的情况下,引用的对象被共享,因此,如果这些对象中的一个被修改,则改变在另外一个中可见。浅拷贝很简单,可以通过简单的完全复制位来实现,所以开销也很小。
比如下面的例子。
>>> a = [[1,2,3], [1,2], [1]]
>>> id(a), id(a[0]), id(a[1]), id(a[2])
(4420406880, 4420406448, 4420406808, 4420406304)
>>> b = copy.copy(a)
>>> id(b), id(b[0]), id(b[1]), id(b[2])
(4420423760, 4420406448, 4420406808, 4420406304)
深拷贝
深拷贝另一种方法是深拷贝,这意味着原对象中的字段被解引用。和浅拷贝不同,引用不再被拷贝,而是会为引用的值创建新的对象。也就是说对于副本的改变不会影响到原始数据。copy
模块中的deepcopy
函数可以实现这一点。
>>> import copy
>>> l1 = [2, 3, 4, [3, 5]]
>>> l2 = copy.deepcopy(l1)
>>> l1, l2
([2, 3, 4, [3, 5]], [2, 3, 4, [3, 5]])
>>> l2[3].append(6)
>>> l2.append(7)
>>> l1, l2
([2, 3, 4, [3, 5]], [2, 3, 4, [3, 5, 6], 7])
总结
Python中copy
模块通过copy()
和deepcopy()
函数来分别提供浅拷贝和深拷贝功能。程序员可以通过在对象中定义__copy__()
和__deepcopy()__
方法来修改模块默认的实现。