• 直接赋值:即对象的引用,其对象的id不变。
  • 浅拷贝(copy):拷贝父对象,不会拷贝对象的内部的子对象。
  • 深拷贝(deepcopy):拷贝父对象及其子对象。

Python 中的id()函数用于返回对象的内存地址。

1.直接赋值

直接赋值,即赋值引用,两个变量指向同一对象(同一内存地址)。如下所示:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
>>> a = [1, 2, 3]
>>> b = a
>>> id(a),id(b)
(139794901177544, 139794901177544)
>>> a[0] = 11
>>> b
[11, 2, 3]
>>> b[1] = 22
>>> a
[11, 22, 3]
>>> id(a),id(b)
(139794901177544, 139794901177544)
>>> 

可以看到,一开始abid相同,它们指向同一内存地址。现在令a[0] = 11,那么b的内容也被同时改变,改变b[1] = 22,同样的a的内容也被同时改变,最后它们的id仍是相同的,再次说明它们的内存地址相同,即指向同一内存地址。

2.浅拷贝

浅拷贝只会拷贝父对象,不会拷贝对象的内部的子对象。如下所示:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
>>> a = [1, 2, 3]
>>> c = a.copy()
>>> id(a)==id(c)
False
>>> c[1] = 2222
>>> a
[1, 2, 3]
>>> c
[1, 2222, 3]
>>> 

因为c浅拷贝了a,所以新增了一个内存地址用于存放c,它们的id不同。改变其c中某个元素的值,a也没有发生相应的改变,这就是因为内存地址不同了。

对于父对象和子对象,例如在a = [1, 2, [3, 4]]中,我们把a[0]a[1]a[2]叫做父对象,把a[2][0]a[2][1]叫做子对象。

但是看下面的例子,如下所示:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
>>> a = [1, 2, [3, 4]]
>>> d = a.copy()
>>> id(a)==id(d)
False
>>> id(a[2])==id(d[2])
True
>>> a[0] = 111
>>> d
[1, 2, [3, 4]]
>>> a[2][0] = 333
>>> d
[1, 2, [333, 4]]
>>> 

一开始d拷贝了a,因为新增了一个内存地址用于存放d,所以id不同了,内存地址也就不同了。这时候改变a[0]这一父对象,相应的d[0]没有改变,说明父对象的内存地址发生改变了,id也不一样了;而改变子对象a[2][0]元素,相应的d[2][0]元素被改变了,说明子对象的内存地址不变,id是相同的。

所以说:浅拷贝会拷贝父对象,不会拷贝子对象。

3.深拷贝

在使用深拷贝之前,需要导入copy模块,即import copy

深拷贝完全拷贝了父对象及子对象。如下所示:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
>>> import copy
>>> a = [1, 2, [3, 4]]
>>> e = copy.deepcopy(a)
>>> e
[1, 2, [3, 4]]
>>> a[0] = 111
>>> e
[1, 2, [3, 4]]
>>> a[2][0] = 333
>>> e
[1, 2, [3, 4]]

这里e深拷贝了a,改变a中某个元素的值,e中的元素不会发生相应的改变,说明aeid不同,内存地址也就不同。

4.总结

  • Python 中对象的赋值都是进行对象引用(内存地址)的传递。
  • 浅拷贝它拷贝了对象,但对于对象中的元素,依然使用的是原始的引用(内存地址),但所含元素(父对象)的子元素(子对象)不会被拷贝。
  • 深拷贝可将父对象和子对象进行拷贝。