使用new_list=my_list时,对new_list的任何修改都会每次更改my_list。为什么会出现这种情况,以及如何克隆或复制列表以防止出现这种情况?
当前回答
菲利克斯已经给出了一个很好的答案,但我想我应该对各种方法进行速度比较:
10.59秒(105.9µs/itn)-copy.depcopy(旧列表)10.16秒(101.6µs/itn)-纯Python Copy()方法使用deepcopy复制类1.488秒(14.88µs/itn)-纯Python Copy()方法不复制类(仅dicts/lists/tuples)0.325秒(3.25µs/itn)-对于old_list:new_list.append(项目)中的项目0.217秒(2.17µs/itn)-[i代表old_list](列表理解)0.186秒(1.86µs/itn)-复制副本(old_list)0.075秒(0.75µs/itn)-列表(旧列表)0.053秒(0.53µs/itn)-新列表=[];新列表扩展(旧列表)0.039秒(0.39µs/itn)-old_list[:](列表切片)
所以最快的是列表切片。但请注意,与copy.deepcopy()和python版本不同,copy.copy()、list[:]和list(list)不会复制列表中的任何列表、字典和类实例,因此如果原始列表发生变化,它们也会在复制的列表中发生变化,反之亦然。
(如果有人感兴趣或想提出任何问题,以下是脚本:)
from copy import deepcopy
class old_class:
def __init__(self):
self.blah = 'blah'
class new_class(object):
def __init__(self):
self.blah = 'blah'
dignore = {str: None, unicode: None, int: None, type(None): None}
def Copy(obj, use_deepcopy=True):
t = type(obj)
if t in (list, tuple):
if t == tuple:
# Convert to a list if a tuple to
# allow assigning to when copying
is_tuple = True
obj = list(obj)
else:
# Otherwise just do a quick slice copy
obj = obj[:]
is_tuple = False
# Copy each item recursively
for x in xrange(len(obj)):
if type(obj[x]) in dignore:
continue
obj[x] = Copy(obj[x], use_deepcopy)
if is_tuple:
# Convert back into a tuple again
obj = tuple(obj)
elif t == dict:
# Use the fast shallow dict copy() method and copy any
# values which aren't immutable (like lists, dicts etc)
obj = obj.copy()
for k in obj:
if type(obj[k]) in dignore:
continue
obj[k] = Copy(obj[k], use_deepcopy)
elif t in dignore:
# Numeric or string/unicode?
# It's immutable, so ignore it!
pass
elif use_deepcopy:
obj = deepcopy(obj)
return obj
if __name__ == '__main__':
import copy
from time import time
num_times = 100000
L = [None, 'blah', 1, 543.4532,
['foo'], ('bar',), {'blah': 'blah'},
old_class(), new_class()]
t = time()
for i in xrange(num_times):
Copy(L)
print 'Custom Copy:', time()-t
t = time()
for i in xrange(num_times):
Copy(L, use_deepcopy=False)
print 'Custom Copy Only Copying Lists/Tuples/Dicts (no classes):', time()-t
t = time()
for i in xrange(num_times):
copy.copy(L)
print 'copy.copy:', time()-t
t = time()
for i in xrange(num_times):
copy.deepcopy(L)
print 'copy.deepcopy:', time()-t
t = time()
for i in xrange(num_times):
L[:]
print 'list slicing [:]:', time()-t
t = time()
for i in xrange(num_times):
list(L)
print 'list(L):', time()-t
t = time()
for i in xrange(num_times):
[i for i in L]
print 'list expression(L):', time()-t
t = time()
for i in xrange(num_times):
a = []
a.extend(L)
print 'list extend:', time()-t
t = time()
for i in xrange(num_times):
a = []
for y in L:
a.append(y)
print 'list append:', time()-t
t = time()
for i in xrange(num_times):
a = []
a.extend(i for i in L)
print 'generator expression extend:', time()-t
其他回答
请注意,在某些情况下,如果您定义了自己的自定义类,并且希望保留这些属性,则应使用copy.copy()或copy.deepcopy(),而不是其他选项,例如在Python 3中:
import copy
class MyList(list):
pass
lst = MyList([1,2,3])
lst.name = 'custom list'
d = {
'original': lst,
'slicecopy' : lst[:],
'lstcopy' : lst.copy(),
'copycopy': copy.copy(lst),
'deepcopy': copy.deepcopy(lst)
}
for k,v in d.items():
print('lst: {}'.format(k), end=', ')
try:
name = v.name
except AttributeError:
name = 'NA'
print('name: {}'.format(name))
输出:
lst: original, name: custom list
lst: slicecopy, name: NA
lst: lstcopy, name: NA
lst: copycopy, name: custom list
lst: deepcopy, name: custom list
new_list=my_list实际上并没有创建第二个列表。赋值只是将引用复制到列表,而不是实际的列表,因此new_list和my_list在赋值后都引用相同的列表。
要实际复制列表,您有几个选项:
您可以使用内置的list.copy()方法(从Python 3.3开始提供):new_list=old_list.copy()您可以对其进行切片:new_list=旧列表[:]亚历克斯·马特利(Alex Martelli)(至少在2007年)对此的看法是,这是一种奇怪的语法,永远使用它都没有意义(在他看来,下一篇更具可读性)。您可以使用内置的list()构造函数:new_list=列表(old_list)您可以使用泛型copy.copy():导入副本new_list=复制副本(old_list)这比list()慢一点,因为它必须首先找到old_list的数据类型。如果您还需要复制列表中的元素,请使用genericcopy.deepcopy():导入副本new_list=复制.depcopy(old_list)显然是最慢、最需要内存的方法,但有时不可避免。这是递归操作;它将处理任意级别的嵌套列表(或其他容器)。
例子:
import copy
class Foo(object):
def __init__(self, val):
self.val = val
def __repr__(self):
return f'Foo({self.val!r})'
foo = Foo(1)
a = ['foo', foo]
b = a.copy()
c = a[:]
d = list(a)
e = copy.copy(a)
f = copy.deepcopy(a)
# edit orignal list and instance
a.append('baz')
foo.val = 5
print(f'original: {a}\nlist.copy(): {b}\nslice: {c}\nlist(): {d}\ncopy: {e}\ndeepcopy: {f}')
结果:
original: ['foo', Foo(5), 'baz']
list.copy(): ['foo', Foo(5)]
slice: ['foo', Foo(5)]
list(): ['foo', Foo(5)]
copy: ['foo', Foo(5)]
deepcopy: ['foo', Foo(1)]
对每种复制模式的简短解释:
浅层副本构造一个新的复合对象,然后(在可能的范围内)向其中插入对原始对象的引用-创建浅层副本:
new_list = my_list
深度副本构造一个新的复合对象,然后递归地将原始对象的副本插入其中,从而创建一个深度副本:
new_list = list(my_list)
list()适用于简单列表的深度复制,例如:
my_list = ["A","B","C"]
但是,对于复杂的列表,如。。。
my_complex_list = [{'A' : 500, 'B' : 501},{'C' : 502}]
…使用deepcopy():
import copy
new_complex_list = copy.deepcopy(my_complex_list)
这是因为,行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)
所有其他贡献者都给出了很好的答案,当你有一个单一维度(水平化)列表时,这些方法是有效的,但是在目前提到的方法中,只有copy.deepcopy()可以克隆/复制列表,而当你使用多维嵌套列表(列表列表)时,它不会指向嵌套列表对象。虽然菲利克斯·克林在他的回答中提到了这一点,但这个问题还有一点问题,可能还有一个使用内置程序的解决方案,这可能会证明是深度复制的更快替代方案。
虽然new_list=old_list[:],copy.copy(old_list)'和Py3k old_list.copy()适用于单层列表,但它们恢复为指向嵌套在old_list和new_list中的列表对象,对其中一个列表对象的更改将在另一个列表中永久化。
编辑:新信息曝光
正如Aaron Hall和PM 2Ring所指出的那样,使用eval()不仅是一个坏主意,而且比copy.deepcopy()慢得多。这意味着,对于多维列表,唯一的选项是copy.deepcopy()。尽管如此,当您尝试在中等大小的多维数组上使用它时,它确实不是一个选项,因为性能会下降。我尝试使用42x42阵列来计时,这是前所未闻的,甚至对于生物信息学应用程序来说也是如此之大,我放弃了等待响应,只是开始在这篇文章中输入我的编辑。似乎唯一真正的选择就是初始化多个列表并独立处理它们。如果有人对如何处理多维列表复制有任何其他建议,将不胜感激。
正如其他人所说的那样,在多维列表中使用copy模块和copy.devcopy存在严重的性能问题。
推荐文章
- 如何在Python中进行热编码?
- 如何嵌入HTML到IPython输出?
- 在Python生成器上使用“send”函数的目的是什么?
- 在list中获取不同值的列表
- 是否可以将已编译的.pyc文件反编译为.py文件?
- Django模型表单对象的自动创建日期
- 在Python中包装长行
- 如何计算两个时间串之间的时间间隔
- Android无尽列表
- 我如何才能找到一个Python函数的参数的数量?
- 在c++中,是通过值传递更好,还是通过引用到const传递更好?
- 您可以使用生成器函数来做什么?
- 将Python诗歌与Docker集成
- 提取和保存视频帧
- 使用请求包时出现SSL InsecurePlatform错误