python闲谈----关于Python中列表的赋值与拷贝(浅拷贝、深拷贝)操作
- 列表作为python中最重要也是最基础的序列,以其简单方便的操作被广泛应用,但在引用列表时也要注意一些小坑,这里讨论一下关于列表赋值的操作。
- 《Fluent Python》的作者Luciano Ramalho将序列按照内存模型分为容器序列和扁平序列
容器序列 | list、tuple、collections.deque |
---|---|
扁平序列 | str、bytes、bytearray、memoryview、array.array |
- 容器序列存放的是它们所包含的任意类型的对象的引用,而扁平序列里存放的是值而不是引用。列表就是容器序列的一种,如一个列表可以是
[1,2,3,4]
,也可以是嵌套的[1,2,[3,4]]
列表的"="号赋值操作
案例:
a = [1,2,3,4]
b = a
a
>>> [1, 2, 3, 4]
b
>>> [1, 2, 3, 4]
a[0] = 5 # 将a列表的第一个数据修改为5
a
>>> [5, 2, 3, 4] # a列表发生变化
b
>>> [5, 2, 3, 4] # b列表也跟着改变
为什么修改a列表的数据b列表也跟着变化?
事实上,a = [1,2,3,4]
表示将a指向一个列表的地址,而b = a
表示让b也指向a所指向的地址:
a = [1,2,3,4]操作
b = a操作
因此修改a[0]实际上是在直接对地址中的值修改
由于b也指向列表的地址,因此b也会跟着改变
列表的浅拷贝
案例
import copy
a = [1,2,3,[4,5]]
b = a.copy() # 浅拷贝方法1
# b = copy.copy(a) # 浅拷贝方法2
# b = a[:] # 浅拷贝方法3
a
>>> [1, 2, 3, [4, 5]]
b
>>> [1, 2, 3, [4, 5]]
a[0] = 6 # 修改a列表的第一层数据
a
>>> [6, 2, 3, [4, 5]] # a列表的第一层数据发生改变
b
>>> [1, 2, 3, [4, 5]] # b列表不受影响
a[3][0] = 7 # 修改a列表的第二层数据
a
>>> [6, 2, 3, [7, 5]] # a列表的第二层数据发生改变
b
>>> [1, 2, 3, [7, 5]] # b列表的第二层数据也发生改变
上述案例中1,2,3都属于第一层数据,而4,5因为在嵌套列表中属于第二层数据(如果是多层嵌套,数据层数依次增加)。浅拷贝时会给新的拷贝对象开辟新的空间,但是只会给第一层数据开辟空间,而第二层和更深层次的数据则采用引用的方式。如图所示:
因为a[0]中的数据在第一层,当修改a[0]的数据时,仅仅是修改了a所指向的空间中的数据,b指向空间中的数据并未受影响
而当修改a[3][0]
中的数据时,由于a[3][0]
处于第二层数据,同时被a和b指向,当它发生改变时a和b的数据都会改变
- 因此浅拷贝可以理解为只拷贝第一层的数据,其他层数据只拷贝元素的引用
列表的深拷贝
案例
import copy
a = [1,2,3,[4,5]]
b = copy.deepcopy(a)
a
>>> [1,2,3,[4,5]]
b
>>> [1,2,3,[4,5]]
a[0] = 6 # 修改a列表的第一层数据
a
>>> [6, 2, 3, [4, 5]] # a列表的第一层数据发生改变
b
>>> [1, 2, 3, [4, 5]] # b列表不受影响
a[3][0] = 7 # 修改a列表的第二层数据
a
>>> [6, 2, 3, [7, 5]] # a列表的第二层数据发生改变
b
>>> [1, 2, 3, [4, 5]] # b列表不受影响
- 列表的深拷贝采用
copy.deepcopy(list)
的方式,该方法产生的拷贝无论是第几层数据都完全复制。