如何克隆一个列表,使其在赋值后不会意外改变?
如何克隆一个列表,使其在赋值后不会意外改变?
在使用 new_list = my_list
时,每次对 new_list
进行修改都会改变 my_list
。这是为什么,我该如何克隆或复制列表以避免这种情况?
admin 更改状态以发布 2023年5月25日
Felix已经提供了一个很好的答案,但我想比较一下各种方法的速度:
- 10.59秒(105.9微秒/次) -
copy.deepcopy(old_list)
- 10.16秒(101.6微秒/次) - 用深拷贝拷贝类的纯Python
Copy()
方法 - 1.488秒(14.88微秒/次) - 用
Copy()
方法拷贝字典/列表/元组的纯Python方法(不拷贝类) - 0.325秒(3.25微秒/次) -
for item in old_list: new_list.append(item)
- 0.217秒(2.17微秒/次) -
[i for i in old_list]
(一个列表推导式) - 0.186秒(1.86微秒/次) -
copy.copy(old_list)
- 0.075秒(0.75微秒/次) -
list(old_list)
- 0.053秒(0.53微秒/次) -
new_list = []; new_list.extend(old_list)
- 0.039秒(0.39微秒/次) -
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
new_list = my_list
实际上并没有创建第二个列表。赋值只是复制了列表的引用,而不是实际的列表,所以在赋值后,new_list
和 my_list
都引用同一个列表。
要真正复制列表,有几种选择:
-
你可以使用内置的
list.copy()
方法(自 Python 3.3 起可用):new_list = old_list.copy()
-
你可以使用切片:
new_list = old_list[:]
Alex Martelli 的观点(至少在2007年时)是,这是一种奇怪的语法,没有任何意义。;) (在他看来,下一种方法更易读)。
-
你可以使用内置的
list()
构造函数:new_list = list(old_list)
-
你可以使用通用的
copy.copy()
:import copy new_list = copy.copy(old_list)
这比
list()
稍慢,因为它必须先找到old_list
的数据类型。 -
如果你需要同时复制列表的元素,使用通用的
copy.deepcopy()
:import copy new_list = copy.deepcopy(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} list.copy(): {b} slice: {c} list(): {d} copy: {e} deepcopy: {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)]