在python中,赋值就是建立一个对象的引用,而不是将对象存储为另一个副本。比如:

>>> a=[1,2,3]
>>> b=a
>>> c=a

对象是[1,2,3]。分别由a、b、c三个变量其建立了相应的引用关系。而三个变量都不独占对象[1,2,3],或者说。能够通过不论什么一个变量来改动[1,2,3]这个对象。

>>> c.append(4)
>>> c
[1, 2, 3, 4]
>>> a
[1, 2, 3, 4]
>>> b
[1, 2, 3, 4]
>>> b.append("from b")
>>> b
[1, 2, 3, 4, 'from b']
>>> a
[1, 2, 3, 4, 'from b']
>>> c
[1, 2, 3, 4, 'from b']

拷贝

拷贝有浅拷贝和深拷贝之分。首先看以下的样例

>>> a=[1,2,3] #建立a与对象的引用连接
>>> b=a #通过赋值。b也与对象建立引用
>>> import copy
>>> c=copy.copy(a) #浅拷贝,建立了一个[1,2,3]的副本。即一个新的对象
>>> d=copy.deepcopy(a) #深拷贝,建立了一个[1,2,3]的副本。

至此,a、b、c、d的关系例如以下图所看到的:

a--|
|-->[1,2,3]
b--|
c----->[1,2,3]
d----->[1,2,3]

改动a

>>> a.append('aa') #通过变量a改动原对象,使它变成[1,2,3,'aa']
>>> b #b也同步改变
[1, 2, 3, 'aa']
>>> c #c相应的是一个副本。没有受到影响
[1, 2, 3]
>>> d #d同上道理
[1, 2, 3]
a、b、c、d的关系例如以下图所看到的:
a--|
|-->[1,2,3,'aa']
b--|
c----->[1,2,3]
d----->[1,2,3]

改动c:

>>> c.append("cc") #改动c的对象
>>> a #a没有受到影响。由于a、c是指向不同对象
[1, 2, 3, 'aa']
>>> b
[1, 2, 3, 'aa']
>>> c
[1, 2, 3, 'cc']
>>> d
[1, 2, 3]
a、b、c、d的关系例如以下图所看到的:
a--|
|-->[1,2,3,'aa']
b--|
c----->[1,2,3,'cc']
d----->[1,2,3]

在上面的样例中,似乎copy和deepcopy没有什么差别,都是另外建立一个副本。且看例如以下样例:

>>> q=[1,2,3,['a','b']] #注意,这个对象是一个list,里面另一个元素是list,即q[3]=['a','b']
>>> w=copy.copy(q) #w、e分别是浅拷贝和深拷贝的副本对象引用
>>> e=copy.deepcopy(q)
>>> q.append('4q') #q所相应的[1,2,3,['a','b']]变成[1,2,3,['a','b'],'4q']
>>> q
[1, 2, 3, ['a', 'b'], '4q']
>>> w #w,e如同前面一样。没有受到影响
[1, 2, 3, ['a', 'b']]
>>> e
[1, 2, 3, ['a', 'b']]

改动w的相应列表

>>> w.append(4)
>>> w #添加了一个整数4
[1, 2, 3, ['a', 'b'], 4]
>>> q #q和e都没有受到影响
[1, 2, 3, ['a', 'b']]
>>> e
[1, 2, 3, ['a', 'b']]
>>> q
[1, 2, 3, ['a', 'b']]

改动w[3]的元素。提示:w是对q进行浅拷贝而得。

>>> w[3].append('w3c')
>>> q
[1, 2, 3, ['a', 'b', 'w3c']]
>>> w
[1, 2, 3, ['a', 'b', 'w3c'], 4]
>>> e
[1, 2, 3, ['a', 'b']]

细致观察上面的结果,发现:

q和w。当改动了列表里面的列表元素之后,两个同步改动了。

e没有受到影响

也就是浅拷贝。仅仅建立了外层的副本,对于内层的副本没有建立;而深拷贝。建立了完整的副本。这就理解了汉语的翻译名称“浅”拷贝之含义了,其“浅”就是拷贝了一层(外层)。

进一步改动e看看效果:

>>> e.append('5e')
>>> q
[1, 2, 3, ['a', 'b', 'w3c']]
>>> w
[1, 2, 3, ['a', 'b', 'w3c'], 4]
>>> e
[1, 2, 3, ['a', 'b'], '5e']
>>> e[3].append('e3c')
>>> q
[1, 2, 3, ['a', 'b', 'w3c']]
>>> w
[1, 2, 3, ['a', 'b', 'w3c'], 4]
>>> e
[1, 2, 3, ['a', 'b', 'e3c'], '5e']

有思考的程序猿,看到这里就会提出一个问题。为什么要有浅拷贝和深拷贝呢?它们分别是怎样工作的?