学习python要有一个很强的面向对象思维,用python,那万物皆对象。
今天想写写深浅拷贝的一点基础理解,以list来作为例子解释。
LIST_SOURCE= [
[[1, 1, 1, 1], [11, 11, 11, 11], [111, 111, 111, 111]],
[[2, 2, 2, 2], [22, 22, 22, 22], [222, 222, 222, 222]],
[[3, 3, 3, 3], [33, 33, 33, 33], [333, 333, 333, 333]]
]
首先存在这样一个列表LIST_SOURCE
,总共有三层list
嵌套。现在有一个需求:需要提取 [22, 22, 22, 22]
这个列表数据。
new_list = list[1][1]
# new_list = [22, 22, 22, 22]
很简单,找到对应的index进行赋值,这样就取值了。但是这时候需要对LIST_SOURCE
进行数值修改,将LIST_SOURCE[1][1][1]
数值修改了,改成0
。即:
LIST_SOURCE= [
[[1, 1, 1, 1], [11, 11, 11, 11], [111, 111, 111, 111]],
[[2, 2, 2, 2], [22, 0, 22, 22], [222, 222, 222, 222]],
[[3, 3, 3, 3], [33, 33, 33, 33], [333, 333, 333, 333]]
]
可修改完以后发现new_list
当中的值也给改了。在一些需求当中可能要求不行,要求A改变了但被A赋值的B不能改变,这就是深浅拷贝问题了。
先说说为什么会这样。
开头说了在Python中要学会一点:万物皆对象
可以LIST_SOURCE
进行分解、抽象化一下来进行理解。
list
(即列表类型)是动态可变的,它不像tuple、str、int
等这些类型,写下以后就定死了,要改变除非重新赋值一次。
example = [1, 2, 3, 4] # id(example) = 2119472699144
比如有这样一个列表example
,对其id值进行查看为2119472699144
。对example
进行一些操作,如append、extend、pop等一些方法操作,会发现无论如何它的id
都不会改变,依旧为2119472699144
。所以我们称这样一个类型的数据是动态的数据类型,是一个独立的可变对象。
有以上的基础理解以后,回头再看LIST_SOURCE
这个列表。
我们可以将LIST_SOURCE
抽象理解为多个动态数据组合的。即:
LIST_SOURCE = [
[list1, list2, list3],
[list4, list5, list6],
[list7, list8, list9]
]
或者换一种写法
LIST_SOURCE = [
[对象1, 对象2, 对象3],
[对象4, 对象5, 对象6],
[对象7, 对象8, 对象9],
]
每一个list
都是一个动态可变的数据类型,他们都有一个唯一id
,他们都是一个独立的对象。那么将LIST_SOURCE[1][1]
取出来我们就可以理解为将一个"对象"
取出来,其实就是将这个"对象"
的id
取出来,然后将id
赋值到一个新的变量上去,可以用代码来抽象表示一下:
a = b = 1
或者
a = b = [1, 2, 3]
就好比这样一个赋值操作,a
和b
都指向同一个数据,也就是说LIST_SOURCE[1][1]
这个列表对象的地址赋值给了new_list
,自然而然对LIST_SOURCE[1][1]
这个列表当中的值修改以后new_list
也会跟着改变。
在python中许多种操作都可以视为浅拷贝,如上面的情况,或者切片操作,或者标准库当中的copy.copy
方法。其实切片操作这么说不太精确,而是对可变类型数据的切片操作。对字符串等这种不可变类型的切片操作并不能作为浅拷贝,解释器会为这种操作申请新的内存。
为什么会有浅拷贝这种存在呢?当然,存在即合理,我刚开始学习python的时候看一些教学中有提及过这种情况,即:或子母卡的情况,比如母亲有一张银行卡母卡,而儿子有一张子卡,若儿子进行刷卡消费,母卡当中的数据也应该跟着改变,这时候浅拷贝的用途就体现出来了。
至于深拷贝就是利用copy.deepcopy
进行操作,申请一个新的内存空间来存放数据,就不多解释了。主要还是对浅拷贝为什么做一个理解。