在Python中克隆或复制列表有哪些选项?
运用 new_list = my_list
然后修改 new_list
每次 my_list
变化。
为什么是这样?
在Python中克隆或复制列表有哪些选项?
运用 new_list = my_list
然后修改 new_list
每次 my_list
变化。
为什么是这样?
同 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 str(self.val)
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('original: %r\n list.copy(): %r\n slice: %r\n list(): %r\n copy: %r\n deepcopy: %r'
% (a, b, c, d, e, f))
结果:
original: ['foo', 5, 'baz']
list.copy(): ['foo', 5]
slice: ['foo', 5]
list(): ['foo', 5]
copy: ['foo', 5]
deepcopy: ['foo', 1]
同 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 str(self.val)
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('original: %r\n list.copy(): %r\n slice: %r\n list(): %r\n copy: %r\n deepcopy: %r'
% (a, b, c, d, e, f))
结果:
original: ['foo', 5, 'baz']
list.copy(): ['foo', 5]
slice: ['foo', 5]
list(): ['foo', 5]
copy: ['foo', 5]
deepcopy: ['foo', 1]
菲利克斯已经提供了一个很好的答案,但我想我会对各种方法进行速度比较:
copy.deepcopy(old_list)
Copy()
使用deepcopy复制类的方法Copy()
方法不复制类(只有dicts / lists / tuples)for item in old_list: new_list.append(item)
[i for i in old_list]
(一个 列表理解)copy.copy(old_list)
list(old_list)
new_list = []; new_list.extend(old_list)
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
编辑:在基准测试中添加了新式的旧式类和dicts,并使python版本更快,并添加了更多方法,包括列表表达式和 extend()
。
我有 被告知 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中,制作列表的浅表副本的惯用方法是使用原始的完整片段:
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中,列表得到了 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中,使用 deepcopy
在里面 copy
模:
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
有很多答案已经告诉你如何制作一个正确的副本,但没有人说你为什么原来的'副本'失败了。
Python不会将值存储在变量中;它将名称绑定到对象。您的原始作业采用了所引用的对象 my_list
并限制它 new_list
同样。无论您使用哪个名称,仍然只有一个列表,因此在将其引用时会进行更改 my_list
在提到它时会坚持下去 new_list
。此问题的其他每个答案都为您提供了创建要绑定的新对象的不同方法 new_list
。
列表的每个元素都像一个名称,因为每个元素都非唯一地绑定到一个对象。浅拷贝创建一个新列表,其元素绑定到与以前相同的对象。
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]
这还不是一个深层副本,因为列表的每个元素都可以引用其他对象,就像列表绑定到它的元素一样。以递归方式复制列表中的每个元素,然后复制每个元素引用的每个其他对象,依此类推:执行深层复制。
import copy
# each element must have __deepcopy__ defined for this...
new_list = copy.deepcopy(my_list)
看到 文件 有关复制中的角落案例的更多信息。
new_list = list(old_list)
使用 thing[:]
>>> a = [1,2]
>>> b = a[:]
>>> a += [3]
>>> a
[1, 2, 3]
>>> b
[1, 2]
>>>
Python这样做的习惯是 newList = oldList[:]
所有其他贡献者都给了 大 答案,当你有一个单一的维度(水平)列表,但是到目前为止提到的方法时,它才有用 copy.deepcopy()
用于克隆/复制列表,而不是指向嵌套 list
处理多维嵌套列表(列表列表)时的对象。而 费利克斯克林 在他的回答中提到它,这个问题还有一些问题,可能还有一个使用内置函数的解决方法可能是一个更快的替代方法。 deepcopy
。
而 new_list = old_list[:]
, copy.copy(old_list)'
并为Py3k old_list.copy()
为单级列表工作,他们恢复指向 list
嵌套在。中的对象 old_list
和 new_list
,并改变其中之一 list
对象在另一个中永久存在。
正如两者所指出的那样 亚伦霍尔 和 PM 2Ring 运用
eval()
不仅是一个坏主意,它也比它慢得多copy.deepcopy()
。这意味着对于多维列表,唯一的选择是
copy.deepcopy()
。说到这一点,当你尝试在中等大小的多维数组上使用它时,它实际上不是一个选项。我尝试过了timeit
使用42x42阵列,对于生物信息学应用程序来说并不是闻所未闻甚至是那么大,我放弃了等待响应并开始在这篇文章中输入我的编辑。似乎唯一真正的选择是初始化多个列表并独立地处理它们。如果有人有任何其他建议,对于如何处理多维列表复制,将不胜感激。
正如其他人所说,那里 可 很重要 使用的性能问题 copy
模块和 copy.deepcopy
对于多维列表。 尝试在不使用的情况下计算出复制多维列表的不同方法 deepcopy
,(我正在研究一个课程的问题,只允许整个算法运行5秒才能获得学分),我想出了一种使用内置函数制作嵌套列表副本的方法让他们指向彼此或在 list
嵌套在它们中的对象。我用了 eval()
和 repr()
在分配中将旧列表的副本放入新列表而不创建旧列表的链接。它采取以下形式:
new_list = eval(repr(old_list))
基本上这是做什么的代表 old_list
作为字符串,然后计算字符串,就好像它是字符串表示的对象。通过这样做,没有链接到原始 list
对象是成的。一个新的 list
创建对象,每个变量指向其自己的独立对象。以下是使用二维嵌套列表的示例。
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]]
虽然这可能不是规范或语法上正确的方法,但它似乎运作良好。我没有测试过性能,但我会猜测 eval()
和 rep()
将比运行更少的开销 deepcopy
将。
以下是使用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()
做法。
请注意,这些方法可以 不 输出除列表以外的任何输入的等效结果。 它们都适用于可切片对象,有些可用于任何可迭代的对象,但仅适用于可切片对象 copy.copy()
适用于任何Python对象。
以下是感兴趣方的测试代码(模板来自这里):
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))
与其他语言不同 变量和价值,Python有 名称和对象。
这个说法:
a = [1,2,3]
意味着给列表(对象)一个名字 a
, 和这个:
b = a
只是提供相同的对象 a
一个新名字 b
,所以每当你做某事时 a
,对象改变因此 b
变化。
制作一个的唯一方法 真 a的副本是 创建一个新对象 像其他答案已经说过的那样。
您可以看到更多相关信息 这里。