Python列表的深浅复制
概述
Python的列表可以复制,但是这里面有浅复制和深复制,我相信有些人不明白什么是深复制和浅复制,今天我们就来谈谈。
= 号复制
#!/usr/bin/env python # -*- coding: UTF-8 -*- # Author: rex.cheny # E-mail: rex.cheny@outlook.com list1 = ["A", "B"] list2 = list1 print("list1: ", list1, "ID: ", id(list1)) print("list2: ", list2, "ID: ", id(list2)) list2.append("C") print("list1: ", list1, "ID: ", id(list1)) print("list2: ", list2, "ID: ", id(list2))
通过在list2中添加一个元素,查看运行结果发现两个列表都变化了。 所以 = 号是列表最简单的复制,其实不能算复制,因为你看两个列表的ID相同。
列表函数copy()复制
下面我们换一种复制方式
#!/usr/bin/env python # -*- coding: UTF-8 -*- # Author: rex.cheny # E-mail: rex.cheny@outlook.com list1 = ["A", "B"] list2 = list1.copy() print("list1: ", list1, "ID: ", id(list1)) print("list2: ", list2, "ID: ", id(list2)) list2.append("C") print("list1: ", list1, "ID: ", id(list1)) print("list2: ", list2, "ID: ", id(list2))
这里才是拷贝是真的复制了,因为它们的ID不同。查看运行结果发现只有list2发生变化,但它是浅拷贝。
[:]复制
我们再看另外一种复制方式。其实严格上说切片不是复制,列表切片后会返回新列表,原列表不变。
#!/usr/bin/env python # -*- coding: UTF-8 -*- # Author: rex.cheny # E-mail: rex.cheny@outlook.com list1 = ["A", "B"] list2 = list1[:] print("list1: ", list1, "ID: ", id(list1)) print("list2: ", list2, "ID: ", id(list2)) list2.append("C") print("list1: ", list1, "ID: ", id(list1)) print("list2: ", list2, "ID: ", id(list2))
运行结果和上面一样
上面演示了3个实例,但是属于浅拷贝的只有第2种复制方式,你可能问,都已经真的拷贝了那怎么还有深复制呢?
列表结构变化演示
这里我们依然使用copy函数,这个函数和使用[:]是一样的,下面有2处变化
#!/usr/bin/env python # -*- coding: UTF-8 -*- # Author: rex.cheny # E-mail: rex.cheny@outlook.com # 在列表中添加一个列表元素 list1 = ["A", "B", [1, 2]] list2 = list1[:] print("list1: ", list1, "ID: ", id(list1)) print("list2: ", list2, "ID: ", id(list2)) # 这里我们修改列表中的列表,对内层列表进行修改 list2[2].append("C") print("list1: ", list1, "ID: ", id(list1)) print("list2: ", list2, "ID: ", id(list2))
查看运行结果,你发现内层列表都修改了。你没看错。继续向下看。
那如果在这种列表嵌套的场景中也真的复制成独立互不干涉的怎么办呢?这就用到深复制。
deepcopy函数
#!/usr/bin/env python # -*- coding: UTF-8 -*- # Author: rex.cheny # E-mail: rex.cheny@outlook.com # 导入该模块 import copy # 在列表中添加一个列表元素 list1 = ["A", "B", [1, 2]] # 深复制 list2 = copy.deepcopy(list1) print("list1: ", list1, "ID: ", id(list1)) print("list2: ", list2, "ID: ", id(list2)) # 这里我们修改列表中的列表,对内层列表进行修改 list2[2].append("C") print("list1: ", list1, "ID: ", id(list1)) print("list2: ", list2, "ID: ", id(list2))
查看运行结果,符合预期。
什么是浅复制和深复制以及两者区别是什么
其实从效果上看你应该以及明白了两者区别,但要说这个问题就要说数据类型,数据类型分两类:
- 基本数据类型,包括字符串、数字、布尔。存储的是真实数据。
- 对象数据类型(引用数据类型),就是对象,类的实例,存储的是对象的引用。
赋值语句 = 意味着什么呢?比如 name = "tom" 赋值语句(=)的作用就是将变量名和一个内存对象进行绑定,如果对象存在就直接绑定,如果不存在就先创建在绑定。name保存的是指向"tom"的指针,而存储tom的内存区域存储的就是tom这个真实数据;list1 = [1, 2] 这种list1存储的是列表对象的指针,但是列表对象本身并不存储数据而是对其他对象的引用,这里的列表包含1和2两个对象,这两个对象是在内存真实存在的,列表对象只是说引用了这些对象而已,列表也仅仅是一种数据的组织形式。
浅复制:复制指针指向,数据在内存中还只是一份,但是只复制一级对象,如果再有一个引用数据类型就不行了
深复制:可以理解为递归浅复制
如果还不明白就看下面的图
下图是使用深度拷贝后的图示
总结
要想生成两个互不干涉的列表就用deepcopy,其他情况任选。