如何克隆或复制列表?

有哪些选项可以在Python中复制或复制列表?

使用new_list = my_list然后new_list每次my_list改变时修改new_list
为什么是这样?

new_list = my_list ,你实际上没有两个列表。 分配只是将引用复制到列表中,而不是实际的列表,所以new_listmy_list在分配之后引用相同的列表。

要真正复制列表,您有各种可能性:

  • 你可以切片:

     new_list = old_list[:] 

    亚历克斯·马尔泰利 ( Alex Martelli)的观点(至less在2007年 )是这样的, 它是一个奇怪的语法,它永远不会被使用 。 ;)(在他看来,下一个更可读)。

  • 你可以使用内置的list()函数:

     new_list = list(old_list) 
  • 你可以使用通用的copy.copy()

     import copy new_list = copy.copy(old_list) 

    这比list()慢一点,因为它必须先找出old_list的数据types。

  • 如果列表包含对象,并且您想要复制它们,请使用generic 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 str(self.val) foo = Foo(1) a = ['foo', foo] b = a[:] c = list(a) d = copy.copy(a) e = copy.deepcopy(a) # edit orignal list and instance a.append('baz') foo.val = 5 print('original: %r\n slice: %r\n list(): %r\n copy: %r\n deepcopy: %r' % (a, b, c, d, e)) 

结果:

 original: ['foo', 5, 'baz'] slice: ['foo', 5] list(): ['foo', 5] copy: ['foo', 5] deepcopy: ['foo', 1] 

费利克斯已经提供了一个很好的答案,但我想我会做各种方法的速度比较:

  1. 10.59秒(105.9us / itn) – copy.deepcopy(old_list)
  2. 10.16秒(101.6us / itn) – 纯粹的python Copy()方法用deepcopy复制类
  3. 1.488秒(14.88us / itn) – 纯粹的python Copy()方法不复制类(只有字典/列表/元组)
  4. 0.325秒(3.25us / itn) – for item in old_list: new_list.append(item)
  5. 0.217秒(2.17us / itn) – [i for i in old_list] ( 列表理解 )
  6. 0.186秒(1.86us / itn) – copy.copy(old_list)
  7. 0.075秒(0.75us / itn) – list(old_list)
  8. 0.053秒(0.53us / itn) – new_list = []; new_list.extend(old_list) new_list = []; new_list.extend(old_list)
  9. 0.039秒(0.39us / itn) – old_list[:] ( 列表切片 )

所以最快的是列表切片。 但请注意copy.copy()list[:]list(list) ,不像copy.deepcopy()和python版本,不要复制列表中的任何列表,字典和类实例,所以如果原始文件,他们也会在复制列表中更改,反之亦然。

(这是脚本如果任何人有兴趣或想提出任何问题:)

 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 

编辑 :添加新式,老式的类和字典的基准,并使python版本更快,并添加了一些更多的方法,包括列表expression式和extend()

我听说Python 3.3+ 增加了list.copy()方法,它应该和切片一样快:

newlist = old_list.copy()

有哪些选项可以在Python中复制或复制列表?

有两种语义方法来复制列表。 浅拷贝创build相同对象的新列表,深拷贝创build包含等同对象的新列表。

浅列表复制

一个浅拷贝只拷贝列表本身,它是列表中对象的引用的容器。 如果包含的对象本身是可变的,而且其中一个被更改,则更改将反映在两个列表中。

在Python 2和Python 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。 为什么是这样?

my_list是一个指向内存中实际列表的指针。 当你说new_list = my_list你没有复制,你只是添加另一个名字,指向内存中的原始列表。 当我们复制列表时,我们可能会遇到类似的问题。

 >>> l = [[], [], []] >>> l_copy = l[:] >>> l_copy [[], [], []] >>> l_copy[0].append('foo') >>> l_copy [['foo'], [], []] >>> l [['foo'], [], []] 

该列表只是指向内容的指针数组,所以浅拷贝只是复制指针,所以你有两个不同的列表,但是它们有相同的内容。 要制作内容的副本,您需要深层复制。

深拷贝

要在Python 2或3中创build列表的深层副本,请在copy模块中使用deepcopy

 import copy a_deep_copy = copy.deepcopy(a_list) 

为了演示如何让我们创build新的子列表:

 >>> import copy >>> l [['foo'], [], []] >>> l_deep_copy = copy.deepcopy(l) >>> l_deep_copy[0].pop() 'foo' >>> l_deep_copy [[], [], []] >>> l [['foo'], [], []] 

所以我们看到深度复制列表与原始列表完全不同。 你可以推出你自己的function – 但不要。 你很可能会使用标准库的深度拷贝function来创build一些错误。

不要使用eval

您可能会看到这被用作深度复制的方式,但不要这样做:

 problematic_deep_copy = eval(repr(a_list)) 
  1. 这是很危险的,特别是如果你从一个你不信任的来源评估某些东西。
  2. 这是不可靠的,如果你正在复制的子元素没有一个expression式可以被评估重现一个等效的元素。
  3. 这也是性能较差。

在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 

这个答案只适用于Python 2.我还没有升级到Python 3。

已经有很多答案告诉你如何做一个适当的副本,但没有人说为什么你原来的“副本”失败。

Python不会将值存储在variables中; 它将名称绑定到对象。 你原来的任务接受了my_list引用的对象,并将其绑定到new_list 。 无论您使用哪个名称,仍然只有一个列表,因此将其引用为my_list时所做的更改将在以new_list引用时new_list 。 这个问题的其他答案给你不同的方式来创build一个新的对象绑定到new_list

列表中的每个元素都像名字一样,每个元素都非唯一地绑定到一个对象上。 浅拷贝创build一个新的列表,其元素和之前一样绑定到相同的对象上。

 new_list = list(my_list) # or my_list[:], but I prefer this syntax # is simply a shorter way of: new_list = [element for element in my_list] 

要进一步将您的列表复制一份,复制列表引用的每个对象,然后将这些元素副本绑定到新列表。

 import copy # each element must have __copy__ defined for this... new_list = [copy.copy(element) for element in my_list] 

这还不是一个深层复制,因为列表中的每个元素都可能引用其他对象,就像列表绑定到其元素一样。 要recursion复制列表中的每个元素,然后复制每个元素引用的每个其他对象,依此类推:执行深度复制。

 import copy # each element must have __deepcopy__ defined for this... new_list = copy.deepcopy(my_list) 

有关复制中的转angular情况的更多信息,请参阅文档 。

new_list = list(old_list)

Python的成语是newList = oldList[:]

使用thing[:]

 >>> a = [1,2] >>> b = a[:] >>> a += [3] >>> a [1, 2, 3] >>> b [1, 2] >>> 

所有其他的贡献者给出了很好的答案,当你有一个单一的维(列),但是迄今为止提到的方法,只有copy.deepcopy()工作克隆/复制一个列表,而不是指向当您使用多维嵌套列表(列表列表)时,嵌套list对象。 虽然费利克斯·克林在他的回答中提到了这个问题,但是这个问题还有一些问题,可能还有一种解决方法是使用内置的方法,这可能是一种更快的方法。

虽然new_list = old_list[:]copy.copy(old_list)'和Py3k old_list.copy()为单层列表工作,他们恢复指向嵌套在old_listnew_list内的list对象,并更改为一list对象在另一个中是永久存在的。

编辑:新的信息曝光

正如Aaron Hall和PM 2Ring所指出的那样, 使用eval()不仅是一个坏主意,它也比copy.deepcopy()慢得多。

这意味着对于多维列表,唯一的select是copy.deepcopy() 。 说到这一点,当你尝试在中等大小的multidimensional array上使用它时,它并不是一个好的select。 我尝试使用42x42arrays,这在生物信息学应用程序中不是闻所未闻,甚至不是那么大,我放弃了等待响应,并开始在这篇文章中input我的编辑。

似乎唯一真正的select就是初始化多个列表并独立处理它们。 如果有人有任何其他的build议,如何处理多维列表复制,将不胜感激。

正如其他人所说,使用copy模块和copy.deepcopy 多维列表 可能会有 重大的性能问题。 试图找出一种复制多维列表而不使用deepcopy拷贝的不同方式(我正在研究一个课程的问题,为了获得学分只能让整个algorithm运行5秒),我想出了一个办法使用内置函数来创build嵌套列表的副本,而不使它们彼此指向或嵌套在list中的list对象。 我在赋值中使用了eval()repr() ,使旧列表的副本进入新列表,而不创build链接到旧列表。 它采取的forms是:

 new_list = eval(repr(old_list)) 

基本上这是做一个old_list作为一个string的表示,然后评估string,就好像它是string表示的对象。 通过这样做,没有到原始list对象的链接。 一个新的list对象被创build,每个variables指向它自己的独立对象。 这里是一个使用2维嵌套列表的例子。

 old_list = [[0 for j in range(y)] for i in range(x)] # initialize (x,y) nested list # assign a copy of old_list to new list without them pointing to the same list object new_list = eval(repr(old_list)) # make a change to new_list for j in range(y): for i in range(x): new_list[i][j] += 1 

如果你然后检查每个列表的内容,例如一个4乘3列表,Python将返回

 >>> new_list [[1, 1, 1], [1, 1, 1], [1, 1, 1], [1, 1, 1]] >>> old_list [[0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0]] 

虽然这可能不是规范或语法正确的方法,但它似乎运作良好。 我没有testing性能,但是我会猜测eval()rep()运行开销比deepcopy要less。

Python 3.6.0定时

这里是使用Python 3.6.0的计时结果。 请记住,这些时间是相互的,而不是绝对的。

我坚持只做浅拷贝,还添加了一些Python2中不可能实现的新方法,例如list.copy() (Python3的等价物 )和列表解包 ( *new_list, = list ):

 METHOD TIME TAKEN b = a[:] 6.468942025996512 #Python2 winner b = a.copy() 6.986593422974693 #Python3 "slice equivalent" b = []; b.extend(a) 7.309216841997113 b = a[0:len(a)] 10.916740721993847 *b, = a 11.046738261007704 b = list(a) 11.761539687984623 b = [i for i in a] 24.66165203397395 b = copy.copy(a) 30.853400873980718 b = [] for item in a: b.append(item) 48.19176080400939 

考虑到Python3 list.copy()方法的可读性越来越高,我们可以看到老赢家仍然排在前列,但并不是真正的数额巨大。

请注意,这些方法不会输出除列表之外的任何input的等效结果。 它们都适用于可分片的对象,对于任何可迭代都适用,但只有copy.copy()适用于任何Python对象。


这里是感兴趣的人的testing代码( 来自这里的模板 ):

 import timeit COUNT = 50000000 print("Array duplicating. Tests run", COUNT, "times") setup = 'a = [0,1,2,3,4,5,6,7,8,9]; import copy' print("b = list(a)\t\t", timeit.timeit(stmt='b = list(a)', setup=setup, number=COUNT)) print("b = copy.copy(a)\t\t", timeit.timeit(stmt='b = copy.copy(a)', setup=setup, number=COUNT)) print("b = a.copy()\t\t", timeit.timeit(stmt='b = a.copy()', setup=setup, number=COUNT)) print("b = a[:]\t\t", timeit.timeit(stmt='b = a[:]', setup=setup, number=COUNT)) print("b = a[0:len(a)]\t", timeit.timeit(stmt='b = a[0:len(a)]', setup=setup, number=COUNT)) print("*b, = a\t", timeit.timeit(stmt='*b, = a', setup=setup, number=COUNT)) print("b = []; b.extend(a)\t", timeit.timeit(stmt='b = []; b.extend(a)', setup=setup, number=COUNT)) print("b = []\nfor item in a: b.append(item)\t", timeit.timeit(stmt='b = []\nfor item in a: b.append(item)', setup=setup, number=COUNT)) print("b = [i for i in a]\t", timeit.timeit(stmt='b = [i for i in a]', setup=setup, number=COUNT)) 

不同于其他语言有variables和值 ,python有名字和对象

 a=[1,2,3] 

意味着给列表(对象)一个名字“a”

 b=a 

只是给同一个对象一个新的名字“b”,所以每当你用a做某事时,对象就会改变,因此b就会改变。

做一个真正的副本的唯一方法就是创build一个像其他答案一样的新对象

你可以在这里看到更多

不知道这是否仍然是实际的,但同样的行为也适用于字典。 看看这个例子。

 a = {'par' : [1,21,3], 'sar' : [5,6,8]} b = a c = a.copy() a['har'] = [1,2,3] a Out[14]: {'har': [1, 2, 3], 'par': [1, 21, 3], 'sar': [5, 6, 8]} b Out[15]: {'har': [1, 2, 3], 'par': [1, 21, 3], 'sar': [5, 6, 8]} c Out[16]: {'par': [1, 21, 3], 'sar': [5, 6, 8]} 
 new_list = my_list[:] 

new_list = my_list试着理解这一点。 假设my_list在位置X的堆内存中,即my_list指向X.现在通过分配new_list = my_list ,让new_list指向X.这就是所谓的浅拷贝。

现在,如果你分配new_list = my_list[:]你只是简单地将my_list的每个对象复制到new_list。 这就是所谓的深拷贝。

另一种方式,你可以做到这一点是:

  • new_list = list(old_list)
  • import copy new_list = copy.deepcopy(old_list)

一个非常简单的方法独立于Python版本已经在给出的​​答案,你可以使用大部分时间(至less我是)缺less:

 new_list = my_list * 1 #Solution 1 when you are not using nested lists 

但是,如果my_list包含其他容器(例如,嵌套列表),则必须使用复制库中上面的答案中build议的其他容器。 例如:

 import copy new_list = copy.deepcopy(my_list) #Solution 2 when you are using nested lists 

奖励 :如果你不想复制元素使用(又名浅拷贝):

 new_list = my_list[:] 

让我们了解解决scheme#1和解决scheme#2之间的区别

 >>> a = range(5) >>> b = a*1 >>> a,b ([0, 1, 2, 3, 4], [0, 1, 2, 3, 4]) >>> a[2] = 55 >>> a,b ([0, 1, 55, 3, 4], [0, 1, 2, 3, 4]) 

正如你所看到的,当我们不使用嵌套列表时,解决scheme#1完美地工作。 让我们来看看当我们将解决scheme#1应用到嵌套列表时会发生什么。

 >>> from copy import deepcopy >>> a = [range(i,i+4) for i in range(3)] >>> a [[0, 1, 2, 3], [1, 2, 3, 4], [2, 3, 4, 5]] >>> b = a*1 >>> c = deepcopy(a) >>> for i in (a, b, c): print i [[0, 1, 2, 3], [1, 2, 3, 4], [2, 3, 4, 5]] [[0, 1, 2, 3], [1, 2, 3, 4], [2, 3, 4, 5]] [[0, 1, 2, 3], [1, 2, 3, 4], [2, 3, 4, 5]] >>> a[2].append('99') >>> for i in (a, b, c): print i [[0, 1, 2, 3], [1, 2, 3, 4], [2, 3, 4, 5, 99]] [[0, 1, 2, 3], [1, 2, 3, 4], [2, 3, 4, 5, 99]] #Solution#1 didn't work in nested list [[0, 1, 2, 3], [1, 2, 3, 4], [2, 3, 4, 5]] #Solution #2 - DeepCopy worked in nested list 

让我们从头开始,探索一下它:

那么假设你有两个列表:

 list_1=['01','98'] list_2=[['01','98']] 

我们必须复制两个列表,现在从第一个列表开始:

所以首先让我们尝试一般的复制方法:

 copy=list_1 

现在,如果你想复制copy_1然后你可能是错的,让我们来看看:

 The id() function shows us that both variables point to the same list object, ie they share this object. 
 print(id(copy)) print(id(list_1)) 

输出:

 4329485320 4329485320 

惊讶吗? 好吧,让我们来探讨一下:

因此,正如我们所知道的python不会将任何东西存储在variables中,Variables只是引用对象和对象存储的值。 这里对象是list但是我们通过两个不同的variables名创build了对同一个对象的两个引用。 所以这两个variables都指向同一个对象:

所以当你做copy=list_1究竟是干什么的:

在这里输入图像描述

这里在图像list_1和副本是两个variables名称,但对象是两个variables是相同的list

因此,如果您尝试修改复制列表,那么它也会修改原始列表,因为列表中只有一个列表,无论您从复制列表还是从原始列表中进行修改,都将修改该列表:

 copy[0]="modify" print(copy) print(list_1) 

输出:

 ['modify', '98'] ['modify', '98'] 

所以它修改了原来的列表:

那么解决scheme是什么?

解答:

现在让我们转到复制列表的第二个pythonic方法:

 copy_1=list_1[:] 

现在这个方法解决了我们在第一个问题中遇到的问题让我们来看看它:

 print(id(copy_1)) print(id(list_1)) 4338792136 4338791432 

因此,我们可以看到我们的两个列表具有不同的id,这意味着两个variables都指向不同的对象,所以实际上在这里是:

在这里输入图像描述

现在让我们尝试修改列表,看看我们是否仍然面临以前的问题:

 copy_1[0]="modify" print(list_1) print(copy_1) 

输出:

 ['01', '98'] ['modify', '98'] 

所以,你可以看到它不是修改原来的列表,它只修改了复制的列表,所以我们可以。

那么现在我想我们已经完成了? 等待我们必须复制第二个嵌套列表,所以让我们试试pythonic的方式:

 copy_2=list_2[:] 

所以list_2应该引用另一个是list_2副本的对象我们来看看:

 print(id((list_2)),id(copy_2)) 

我们得到的结果是:

 4330403592 4330403528 

现在我们可以假设两个列表都指向不同的对象,现在让我们尝试修改它,让我们看看它是给我们想要的:

所以当我们尝试:

 copy_2[0][1]="modify" print(list_2,copy_2) 

它给了我们输出:

 [['01', 'modify']] [['01', 'modify']] 

现在,我们用pythonic的方式,并没有混淆,我们面临同样的问题。

让我们了解它:

所以当我们这样做时:

 copy_2=list_2[:] 

我们实际上只复制外部列表,而不是嵌套列表,所以嵌套列表是两个列表中的同一个对象,让我们来看看:

 print(id(copy_2[0])) print(id(list_2[0])) 

输出:

 4329485832 4329485832 

所以实际上,当我们做copy_2=list_2[:]这是发生了什么事情:

在这里输入图像描述

它创build列表副本,但只有外部列表副本,而不是嵌套列表副本,嵌套列表是相同的两个variables,所以如果您尝试修改嵌套列表,那么它也会修改原始列表,因为嵌套列表对象是相同的两个嵌套列表。

那么解决scheme是什么?

解决scheme是deep copy

 from copy import deepcopy deep=deepcopy(list_2) 

所以现在让我们来检查一下:

 print(id((list_2)),id(deep)) 

输出:

 4322146056 4322148040 

两个id是不同的,现在我们来检查一下嵌套的列表id:

 print(id(deep[0])) print(id(list_2[0])) 

输出:

 4322145992 4322145800 

正如你可以看到两个ID是不同的,所以我们可以假设两个嵌套列performance在指向不同的对象。

所以当你做deep=deepcopy(list_2)实际上发生了什么:

在这里输入图像描述

所以这两个嵌套列表指向不同的对象,他们现在分开复制嵌套列表。

现在让我们尝试修改嵌套列表,看看它是否解决了以前的问题:

所以如果我们这样做:

 deep[0][1]="modify" print(list_2,deep) 

输出:

 [['01', '98']] [['01', 'modify']] 

所以,你可以看到它没有修改原始的嵌套列表,只修改了复制的列表。

如果你喜欢我的详细的答案,让我知道通过upvoting它,如果你有任何疑问realted这个答案,评论:)