我不是资深程序员,不敢说一定就是这样,但是至少自己想明白了,所以写在这里备忘。
由列表深、浅拷贝所想到的
python中,变量名和实际数据是分离的,一个变量名可以在不同时间指向不同数据
id()函数返回的是变量名所指向的那个数据在python内存池(不知道这样说是否恰当)中的id,比如下列代码
>>> a = 1
>>> id(a)
1727161440
>>> id(1)
1727161440
>>>
1和a的id是一样的
接下来是重头戏
列表在python官方解释中,说是“可变数据类型”,也就是说,值改变了,但是id不会变,这是怎么一回事呢?
猜想:列表是一块内存区域,这块内存区域用来指向其他数据,说白了就是下图:
a = [0, 1, 2]
接下来是验证猜想阶段
test_1
定义下面这样的列表:
a = [[1, 2, 3], 2, 3]
猜想其存储方式为:
根据上述猜测,更改a[0][0]和a[1],那么id(a)和id(a[0])不应该变化,id(a[0][0])和id(a[1])应该变化
>>> a=[[1,2,3],2,3]
>>> id(a)
6834776
>>> id(a[0])
8176384
>>> id(a[0][0])
1727161440
>>> id(a[1])
1727161456
>>> a[0][0] = 0
>>> a[1] = 0
>>> id(a)
6834776
>>> id(a[0])
8176384
>>> id(a[0][0])
1727161424
>>> id(a[1])
1727161424
>>>
结果正确
test_2
深拷贝和浅拷贝
浅拷贝:
>>> a=[1,2,3]
>>> b=[4,5,6]
>>> b=a
>>> b[0]=100
>>> a
[100, 2, 3]
>>> b
[100, 2, 3]
>>>
浅拷贝实际上将变量名直接指向了另一个变量名所指向的列表内存空间,也就是下图:
(虚线是更改后的指针)
那么以后操作b,也就实际上是在a所指向的列表上进行操作了。显然,再访问a时,a会发生变化,这一般是我们所不希望看到的
深拷贝
>>> a=[1,2,3]
>>> b=[4,5,6]
>>> for i in range(len(a)):
... b[i] = a[i]
...
>>> b[0] = 100
>>> a
[1, 2, 3]
>>> b
[100, 2, 3]
>>>
要想实现深拷贝,那就应该先创建列表的内存空间,然后更改该内存空间,而不是更改列表名,如下图:
(虚线是更改后的指针)
test_3
关于append()的问题
>>> a=[[1,2,3],2,3]
>>> b=[]
>>> for i in range(len(a)):
... b.append(a[i])
...
>>> b[0][0]=100
>>> a
[[100, 2, 3], 2, 3]
>>> b
[[100, 2, 3], 2, 3]
>>>
append()也是操作 变量名所指向的实际数据块,并没有创建副本。上述代码中,append()直接将a[0]所指向的那个 列表 的内存块加在了b的后面,从而导致了浅拷贝
类似的还有
>>> a=[[1,2,3],2,3]
>>> b=[]
>>> for i in a:
... b.append(i)
...
>>> b[0][0]=100
>>> a
[[100, 2, 3], 2, 3]
>>> b
[[100, 2, 3], 2, 3]
>>>