Python中如何去复制一个列表?

本文参照自Stack Overflow:https://stackoverflow.com/questions/2612802/how-to-clone-or-copy-a-list

如何在Python中复制一个列表?

可能有人会说,这不是很简单的事情吗?直接一个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学习笔记


  • 7
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值