使用new_list=my_list时,对new_list的任何修改都会每次更改my_list。为什么会出现这种情况,以及如何克隆或复制列表以防止出现这种情况?
当前回答
有人告诉我Python 3.3+添加了list.copy()方法,它应该和切片一样快:
newlist = old_list.copy()
其他回答
在Python中克隆或复制列表有哪些选项?
在Python 3中,可以使用以下方法制作浅层副本:
a_copy = a_list.copy()
在Python 2和3中,您可以获得一个浅层副本,其中包含原始文件的完整切片:
a_copy = a_list[:]
解释
复制列表有两种语义方法。浅副本创建相同对象的新列表,深副本创建包含新等效对象的新的列表。
浅表副本
浅层副本仅复制列表本身,它是对列表中对象的引用的容器。如果包含的对象本身是可变的,并且其中一个对象发生了更改,则更改将反映在两个列表中。
在Python 2和3中有不同的方法来实现这一点。Python 2的方式也适用于Python 3。
Python 2
在Python 2中,制作列表的简单副本的惯用方法是使用原始列表的完整片段:
a_copy = a_list[:]
您也可以通过列表构造函数传递列表来完成相同的任务,
a_copy = list(a_list)
但是使用构造函数效率较低:
>>> timeit
>>> l = range(20)
>>> min(timeit.repeat(lambda: l[:]))
0.30504298210144043
>>> min(timeit.repeat(lambda: list(l)))
0.40698814392089844
Python 3
在Python 3中,列表获取list.copy方法:
a_copy = a_list.copy()
在Python 3.5中:
>>> import timeit
>>> l = list(range(20))
>>> min(timeit.repeat(lambda: l[:]))
0.38448613602668047
>>> min(timeit.repeat(lambda: list(l)))
0.6309100328944623
>>> min(timeit.repeat(lambda: l.copy()))
0.38122922903858125
生成另一个指针不会生成副本
使用new_list=my_list,然后在每次my_list更改时修改new_list。这是为什么?
mylist只是一个指向内存中实际列表的名称。当你说new_list=my_list时,你不是在复制,只是在添加另一个指向内存中原始列表的名称。当我们复制列表时,也会遇到类似的问题。
>>> l = [[], [], []]
>>> l_copy = l[:]
>>> l_copy
[[], [], []]
>>> l_copy[0].append('foo')
>>> l_copy
[['foo'], [], []]
>>> l
[['foo'], [], []]
列表只是指向内容的指针数组,因此浅层副本只是复制指针,因此您有两个不同的列表,但它们具有相同的内容。要复制内容,您需要一个深度副本。
深度副本
要制作列表的深度副本,在Python 2或3中,请在复制模块中使用deepcopy:
import copy
a_deep_copy = copy.deepcopy(a_list)
要演示这如何允许我们创建新的子列表:
>>> import copy
>>> l
[['foo'], [], []]
>>> l_deep_copy = copy.deepcopy(l)
>>> l_deep_copy[0].pop()
'foo'
>>> l_deep_copy
[[], [], []]
>>> l
[['foo'], [], []]
所以我们看到,深度复制的列表与原始列表完全不同。你可以滚动自己的函数,但不要。通过使用标准库的deepcopy功能,您很可能会创建一些错误。
不使用eval
你可能会看到这是一种深度复制的方式,但不要这样做:
problematic_deep_copy = eval(repr(a_list))
这是很危险的,特别是当你从一个你不信任的来源评估某件事情时。如果要复制的子元素没有一个可以求值以重现等效元素的表示,那么它就不可靠。它的性能也较差。
在64位Python 2.7中:
>>> import timeit
>>> import copy
>>> l = range(10)
>>> min(timeit.repeat(lambda: copy.deepcopy(l)))
27.55826997756958
>>> min(timeit.repeat(lambda: eval(repr(l))))
29.04534101486206
在64位Python 3.5上:
>>> import timeit
>>> import copy
>>> l = list(range(10))
>>> min(timeit.repeat(lambda: copy.deepcopy(l)))
16.84255409205798
>>> min(timeit.repeat(lambda: eval(repr(l))))
34.813894678023644
使用对象[:]
>>> a = [1,2]
>>> b = a[:]
>>> a += [3]
>>> a
[1, 2, 3]
>>> b
[1, 2]
>>>
Python的习惯用法是newList=oldList[:]
这是因为,行new_list=my_list为变量my_list分配了一个新的引用,即new_list这类似于下面给出的C代码,
int my_list[] = [1,2,3,4];
int *new_list;
new_list = my_list;
您应该使用复制模块创建新列表
import copy
new_list = copy.deepcopy(my_list)
deepcopy选项是唯一适用于我的方法:
from copy import deepcopy
a = [ [ list(range(1, 3)) for i in range(3) ] ]
b = deepcopy(a)
b[0][1]=[3]
print('Deep:')
print(a)
print(b)
print('-----------------------------')
a = [ [ list(range(1, 3)) for i in range(3) ] ]
b = a*1
b[0][1]=[3]
print('*1:')
print(a)
print(b)
print('-----------------------------')
a = [ [ list(range(1, 3)) for i in range(3) ] ]
b = a[:]
b[0][1]=[3]
print('Vector copy:')
print(a)
print(b)
print('-----------------------------')
a = [ [ list(range(1, 3)) for i in range(3) ] ]
b = list(a)
b[0][1]=[3]
print('List copy:')
print(a)
print(b)
print('-----------------------------')
a = [ [ list(range(1, 3)) for i in range(3) ] ]
b = a.copy()
b[0][1]=[3]
print('.copy():')
print(a)
print(b)
print('-----------------------------')
a = [ [ list(range(1, 3)) for i in range(3) ] ]
b = a
b[0][1]=[3]
print('Shallow:')
print(a)
print(b)
print('-----------------------------')
导致输出:
Deep:
[[[1, 2], [1, 2], [1, 2]]]
[[[1, 2], [3], [1, 2]]]
-----------------------------
*1:
[[[1, 2], [3], [1, 2]]]
[[[1, 2], [3], [1, 2]]]
-----------------------------
Vector copy:
[[[1, 2], [3], [1, 2]]]
[[[1, 2], [3], [1, 2]]]
-----------------------------
List copy:
[[[1, 2], [3], [1, 2]]]
[[[1, 2], [3], [1, 2]]]
-----------------------------
.copy():
[[[1, 2], [3], [1, 2]]]
[[[1, 2], [3], [1, 2]]]
-----------------------------
Shallow:
[[[1, 2], [3], [1, 2]]]
[[[1, 2], [3], [1, 2]]]
-----------------------------
推荐文章
- 证书验证失败:无法获得本地颁发者证书
- 当使用pip3安装包时,“Python中的ssl模块不可用”
- 无法切换Python与pyenv
- Python if not == vs if !=
- 如何从scikit-learn决策树中提取决策规则?
- 为什么在Mac OS X v10.9 (Mavericks)的终端中apt-get功能不起作用?
- 将旋转的xtick标签与各自的xtick对齐
- 为什么元组可以包含可变项?
- 如何合并字典的字典?
- 如何创建类属性?
- 不区分大小写的“in”
- 在Python中获取迭代器中的元素个数
- 解析日期字符串并更改格式
- 使用try和。Python中的if
- 如何在Python中获得所有直接子目录