本文参照自Stack Overflow:https://stackoverflow.com/questions/2612802/how-to-clone-or-copy-a-list
可能有人会说,这不是很简单的事情吗?直接一个new_list = old_list
不就好了?
那实际上这样操作是否可行呢?让我们来举个栗子:
1old_list = ['python','小黑屋']
2new_list = old_list
3print(new_list)
4>>>['python', '小黑屋']
5old_list.append("666")
6print(new_list)
7>>>['python', '小黑屋', '666']
是不是觉得很神奇,当我原先的列表发生变化的时候,我新复制的列表也发生了变化,这根本不是我想要的结果啊,就像是我新买了一个手机,却发现他会受别人远程控制,别提心里是什么滋味了!
那么,为什么会出现这种情况呢?
原来,在Pyhton中使用赋值(=)的时候,实际上只是给新变量指向一个引用,简单一点讲就是当我们执行a=b时,实际上进行的操作是将a的内存地址赋值给了b,此时a和b指向的是同一个内存地址,并不是开辟一个新的内存地址给b,所以每当内存地址里的值发生变化的时候,b的值也会随之变化。
如果列表内为不可变数据时可用以下方法:
1 # 方法一:
2new_list1 = old_list[:]
3# 方法二:
4new_list2 = list(old_list)
5# 方法三:
6new_list3 = []
7for item in old_list: new_list3.append(item)
8# 方法四i
9new_list4 = [i for i in old_list]
10# 方法五
11new_list5 = []
12new_list5.extend(old_list)
13# 方法六:
14import copy
15new_list6 = copy.copy(old_list)
我们可以随便抽取其中一种方法来验证下:
1old_list = ['python','小黑屋']
2new_list4 = [i for i in old_list]
3print(new_list4)
4>>>['python','小黑屋']
5old_list.append("666")
6print(new_list4)
7>>>['python','小黑屋']
8print(old_list)
9>>>['python','小黑屋','666']
可以看到,这时候我们复制的列表都是具有独立性的,完全不受原列表的影响,但是,有注意到我的标题吗?列表内为不可变数据类型时可使用。
这时候我们还是举个反例来说明下:
1extra_list = [1,2,3]
2# 在old_list中加入可变列表extra_list
3old_list = ['python','小黑屋',extra_list]
4new_list4 = [i for i in old_list]
5print(new_list4)
6>>>['python','小黑屋',[1,2,3]]
7# 修改extra_list内容
8extra_list.append("666")
9print(new_list4)
10>>>['python','小黑屋',[1,2,3,'666']]
11print(old_list)
12>>>['python','小黑屋',[1,2,3,'666']]
此时你会发现,似乎这又跟我们使用赋值(=)的结果有些类似,但又有所不同。其实在我们使用上面几种复制列表的时候,它实现的是一种浅拷贝。浅拷贝呢,其实比赋值高级一点,就是他会把列表中的值都复制给新对象,但是如果列表中的值是可变数据(列表、字典、函数、类等),他只会复制一个引用。
那么,我们如果拷贝的列表中带有可变数据时该怎么进行复制呢?很简单,有浅拷贝就会有深拷贝,深拷贝呢,就是将所有东西都复制一遍给新对象,解除你的所有后顾之忧。
1import copy
2extra_list = [1,2,3]
3# 在old_list中加入可变列表extra_list
4old_list = ['python','小黑屋',extra_list]
5# 深拷贝
6new_list7 = copy.deepcopy(old_list)
7print(new_list7)
8>>>['python', '小黑屋', [1, 2, 3]]
9# 修改extra_list内容
10extra_list.append("666")
11print(new_list7)
12>>>['python', '小黑屋', [1, 2, 3]]
13print(old_list)
14>>>['python', '小黑屋', [1, 2, 3, '666']]
由上面的例子可以看出,深拷贝是真正的能复制一个列表而没有任何后顾之忧的,既然如此,那我们还要举上面的例子干嘛呢?直接都使用深拷贝不就好了?
我们可以来看下以下数据,是使用各种不同方法拷贝一个同个列表所花费的时间
110.59 sec (105.9us/itn) - copy.deepcopy(old_list)
20.325 sec (3.25us/itn) - for item in old_list: new_list.append(item)
30.217 sec (2.17us/itn) - [i for i in old_list] (a list comprehension)
40.186 sec (1.86us/itn) - copy.copy(old_list)
50.075 sec (0.75us/itn) - list(old_list)
60.053 sec (0.53us/itn) - new_list = []; new_list.extend(old_list)
70.039 sec (0.39us/itn) - old_list[:] (list slicing)
由上面可以看出,使用深拷贝所花费的时间是其他方法的至少30倍以上,这也是我们为什么要举出上面那些方法的原因。
我们平常在复制列表的时候,大多数列表的内容都是不变数据,如果数据量大的话,使用深拷贝就完全没有必要,会大大降低程序运行效率。
所以,我们在使用时可以根据我们的实际情况来选择不同的复制方法,以便提高我们的工作效率。
欢迎大家关注我的微信公众号Python小黑屋
python学习资源/有趣的python文章/python学习笔记