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
在python中,当您复制数据类型时,原始数据类型和复制数据类型为 share the same memory locations . 因此, any changes made to a copy of object gets reflected in the original object . 例如,考虑一下:
my_lst=[1,2,3,4,5] #Python list
print my_lst, ' my_lst (before copy)'
my_lst_copy = my_lst=[1,2,3,4,5] #Simple copy of python list
my_lst_copy[2] = 55 #Copy of python list changed
print my_lst_copy, ' my_lst_copy (copy of python list)'
print my_lst, ' my_lst (after copy)'
>>>[1, 2, 3, 4, 5] my_lst (before copy)
>>>[1, 2, 55, 4, 5] my_lst_copy (copy of python list)
>>>[1, 2, 55, 4, 5] my_lst (after copy)
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
19 回答
使用
thing[:]
Python 3.6.0计时
以下是使用Python 3.6.0的计时结果 . 请记住,这些时间是彼此相对的,而不是绝对的 .
我坚持只做浅拷贝,并且还添加了Python2中不可能的一些新方法,例如
list.copy()
(Python3 slice equivalent)和list unpacking(*new_list, = list
):考虑到Python3
list.copy()
方法的可读性增加,我们可以看到老赢家仍然名列前茅,但实际上并不是很大 .Note that these methods do not output equivalent results for any input other than lists. 它们都适用于可切片对象,一些适用于任何可迭代对象,但只有
copy.copy()
适用于任何Python对象 .以下是感兴趣方的测试代码(Template from here):
我been told那是Python 3.3 adds list.copy()方法,它应该和切片一样快:
newlist = old_list.copy()
使用
new_list = my_list
,您实际上没有两个列表 . 赋值仅复制对列表的引用,而不是实际列表,因此new_list
和my_list
在赋值后引用相同的列表 .要实际复制列表,您有各种可能性:
Alex Martelli's关于这一点的意见(至少back in 2007)是,这是一种奇怪的语法,使用它没有任何意义 . ;)(在他看来,下一个更具可读性) .
这比
list()
慢一点,因为它必须首先找出old_list
的数据类型 .显然是最慢和最需要内存的方法,但有时是不可避免的 .
Example:
结果:
你可以在list()函数中使用bulit:
我认为这段代码会对你有所帮助 .
您将使用python标准库中的deepcopy .
在python中,当您复制数据类型时,原始数据类型和复制数据类型为 share the same memory locations . 因此, any changes made to a copy of object gets reflected in the original object . 例如,考虑一下:
正如您之前注意到的那样,并且在上面的示例中再次注意到,更改复制列表的任何元素
my_list_cp
会更改原始列表my_list
. 这样做的原因是my_list_cp
没有新的任务 .您可以使用python标准库中的deepcopy来抵消上述情况 . 在深层复制中,对象的副本将复制到其他对象中 .
在上面的示例中,您看到复制后
my_lst
未更改 .请注意,在某些情况下,如果您已经定义了自己的自定义类并且想要保留属性,那么您应该使用
copy.copy()
或copy.deepcopy()
而不是替代方法,例如在Python 3中:输出:
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不会将值存储在变量中;它将名称绑定到对象 . 您的原始作业采用了
my_list
引用的对象,并将其绑定到new_list
. 无论您使用哪个名称,仍然只有一个列表,因此在将其称为my_list
时所做的更改将在将其称为new_list
时保留 . 此问题的其他每个答案都为您提供了创建绑定到new_list
的新对象的不同方法 .列表的每个元素都像一个名称,因为每个元素都非唯一地绑定到一个对象 . 浅拷贝创建一个新列表,其元素绑定到与以前相同的对象 .
要使列表副本更进一步,请复制列表引用的每个对象,并将这些元素副本绑定到新列表 .
这还不是一个深层副本,因为列表的每个元素都可以引用其他对象,就像列表绑定到它的元素一样 . 以递归方式复制列表中的每个元素,然后复制每个元素引用的每个其他对象,依此类推:执行深层复制 .
有关复制中的边角情况的更多信息,请参见the documentation .
让我感到惊讶的是,这还没有被提及,所以为了完整起见......
您可以使用"splat operator":
*
执行列表解压缩,这也将复制列表中的元素 .这种方法的明显缺点是它只能在Python 3.5中使用 .
虽然时间明智,但这似乎比其他常用方法表现更好 .
菲利克斯已经提供了一个很好的答案,但我想我会对各种方法进行速度比较:
10.59秒(105.9us / itn) - copy.deepcopy(old_list)
10.16秒(101.6us / itn) - 使用deepcopy复制类的纯python
Copy()
方法1.488秒(14.88us / itn) - 纯python
Copy()
方法不复制类(只有dicts / lists / tuples)0.325秒(3.25us / itn) -
for item in old_list: new_list.append(item)
0.217秒(2.17us / itn) -
[i for i in old_list]
(a list comprehension)0.186秒(1.86us / itn) - copy.copy(old_list)
0.075秒(0.75us / itn) -
list(old_list)
0.053秒(0.53us / itn) -
new_list = []; new_list.extend(old_list)
0.039秒(0.39us / itn) -
old_list[:]
(list slicing)所以最快的是列表切片 . 但要注意
copy.copy()
,list[:]
和list(list)
,不像copy.deepcopy()
和python版本不复制任何列表中的列表,字典和类实例,因此如果原件发生更改,它们也会在复制的列表中更改,反之亦然 .(这是脚本,如果有人有兴趣或想提出任何问题:)
EDIT :在基准测试中添加了新式的旧式类和dicts,并使python版本更快,并添加了更多方法,包括列表表达式和
extend()
.Python的成语是
newList = oldList[:]
所以假设你有两个清单:
我们必须复制这两个列表,现在从第一个列表开始:
所以首先让我们尝试一般的复制方法:
现在如果您认为复制复制了list_1那么您可能错了,让我们检查一下:
输出:
很惊讶?好的,让我们来探索一下:
因此我们知道python不会在变量中存储任何内容,Variables只是引用对象而对象存储该值 . 这里的对象是
list
但是我们用两个不同的变量名创建了对同一个对象的两个引用 . 所以这两个变量都指向同一个对象:所以当你做
copy=list_1
实际上它在做什么:这里的图像list_1和copy是两个变量名,但两个变量的对象相同,即
list
因此,如果您尝试修改复制列表,那么它也将修改原始列表,因为列表只有一个,无论您是从复制列表还是从原始列表中修改该列表,您都将修改该列表:
输出:
所以它修改了原始列表:
现在让我们转到复制列表的第二种pythonic方法:
现在这个方法解决了我们在第一期中遇到的问题让我们检查一下:
因此我们可以看到我们的两个列表都有不同的id,这意味着两个变量都指向不同的对象,所以这里实际发生的是:
现在让我们尝试修改列表,让我们看看我们是否还面临上一个问题:
输出:
因此,您可以看到它没有修改原始列表,它只修改了复制的列表,所以我们可以使用它 .
所以现在我觉得我们已经完成了?等等我们也要复制第二个嵌套列表所以让我们尝试pythonic方式:
所以list_2应该引用另一个对象,它是list_2的副本,让我们检查一下:
我们得到输出:
现在我们可以假设两个列表都指向不同的对象,所以现在让我们尝试修改它,让我们看看它给出了我们想要的东西:
所以当我们尝试:
它给我们输出:
现在,我们使用pythonic方式,这有点令人困惑,但我们仍面临同样的问题 .
我们理解它:
所以当我们这样做时:
我们实际上只复制外部列表,而不是嵌套列表,所以嵌套列表是两个列表的同一个对象,让我们检查:
输出:
所以实际上当我们做
copy_2=list_2[:]
时会发生这种情况:它创建列表的副本,但只有外部列表副本,而不是嵌套列表副本,嵌套列表对于两个变量都是相同的,因此如果您尝试修改嵌套列表,那么它也将修改原始列表,因为嵌套列表对象对于两者都是相同的嵌套列表 .
那么解决方案是什么?
解决方案是
deep copy
现在让我们检查一下:
输出:
两个id都不同,现在让我们检查嵌套列表id:
输出:
正如你可以看到两个id都不同所以我们可以假设两个嵌套列表现在指向不同的对象 .
所以,当你做
deep=deepcopy(list_2)
实际发生的事情:因此,两个嵌套列表都指向不同的对象,并且它们现在具有单独的嵌套列表副本 .
现在让我们尝试修改嵌套列表,让我们看看它是否解决了以前的问题:
所以如果我们这样做:
输出:
因此,您可以看到它没有修改原始嵌套列表,它只修改了复制的列表 .
如果您喜欢我的详细答案,请通过提升来告诉我,如果您有任何疑问,请回答一下,评论:)
在Python 3中,可以使用以下方式创建浅表副本:
在Python 2和3中,您可以获得一个带有原始片段的浅拷贝:
解释
有两种语义方法可以复制列表 . 浅拷贝创建相同对象的新列表,深拷贝创建包含新等效对象的新列表 .
浅名单副本
浅拷贝仅复制列表本身,列表本身是对列表中对象的引用的容器 . 如果包含的对象本身是可变的并且其中一个被更改,则更改将反映在两个列表中 .
在Python 2和3中有不同的方法可以做到这一点.Python 2方法也适用于Python 3 .
Python 2
在Python 2中,制作列表浅表副本的惯用方法是使用完整的原始片段:
你也可以通过列表构造函数传递列表来完成同样的事情,
但使用构造函数效率较低:
Python 3
在Python 3中,列表获取
list.copy
方法:在Python 3.5中:
制作另一个指针不会复制
my_list
只是一个指向内存中实际列表的名称 . 当你说new_list = my_list
时,你只需添加另一个指向内存中原始列表的名称 . 当我们制作列表副本时,我们可能会遇到类似的问题 .该列表只是指向内容的指针数组,因此浅复制只复制指针,因此您有两个不同的列表,但它们具有相同的内容 . 要制作内容的副本,您需要一份深层副本 .
深拷贝
制作deep copy of a list, in Python 2 or 3, use deepcopy in the copy module:
为了演示这如何允许我们创建新的子列表:
因此,我们看到深层复制列表与原始列表完全不同 . 你可以自己动手 - 但不要 . 您可能会使用标准库的deepcopy函数创建您不会遇到的错误 .
不要使用eval
你可能会看到这被用作深度复制的一种方法,但是不要这样做:
它's dangerous, particularly if you'从你不信任的来源评估一些东西 .
它's not reliable, if a subelement you'重新复制doesn 't have a representation that can be eval' d以重现等效元素 .
性能也不太好 .
在64位Python 2.7中:
在64位Python 3.5上:
所有其他贡献者给出了 great 答案,当你有一个维度(水平)列表时,它可以工作,但是到目前为止提到的方法中,只有
copy.deepcopy()
用于克隆/复制列表而不是指向嵌套的list
对象时您正在使用多维嵌套列表(列表列表) . 虽然Felix Kling在他的回答中引用了它,但问题还有一点,可能使用内置函数的解决方法可能是deepcopy
的更快替代方案 .虽然
new_list = old_list[:]
,copy.copy(old_list)'
和Py3kold_list.copy()
适用于单级列表,但它们会返回指向嵌套在old_list
和new_list
内的list
对象,并且对其中一个list
对象的更改在另一个中永久存在 .编辑:新信息曝光
正如其他人所说,使用
copy
模块和copy.deepcopy
for multidimensional lists 可能会出现 are significant 性能问题 . 尝试在不使用深度复制的情况下计算出复制多维列表的不同方法(我正在研究一个课程的问题,只允许整个算法运行5秒才能获得信用),我想出了一个方法使用内置函数制作嵌套列表的副本,而不是将它们指向彼此或嵌套在它们中的列表对象 . 我在赋值中使用了eval()和repr()来将旧列表的副本放入新列表中,而不创建旧列表的链接 . 它采取以下形式:new_list = eval(repr(old_list))
基本上,它的作用是将old_list表示为字符串,然后将字符串计算为字符串所代表的对象 . 通过这样做,不会链接到原始列表对象 . 创建一个新的列表对象,每个变量指向它自己的独立对象 . 以下是使用二维嵌套列表的示例 . 对于范围(x)中的i,old_list = [[0表示范围(y)中的j]] #initialize(x,y)嵌套列表
#将old_list的副本分配给新列表,而不指向同一列表对象
new_list = eval(repr(old_list))
#对new_list进行更改
对于范围内的j(y):
对于范围(x)中的i:
new_list [i] [j] = 1
如果您然后检查每个列表的内容,例如4乘3列表,Python将返回>>> new_list
[[1,1,1],[1,1,1],[1,1,1],[1,1,1]]
[[0,0,0],[0,0,0],[0,0,0],[0,0,0]]
虽然这可能不是规范或语法上正确的方法,但它似乎运作良好 . 我没有测试过性能,但我猜测eval()和rep()的开销会减少运行比deepcopy会 .
new_list = list(old_list)
一个非常简单的方法独立于python版本在已经给出的答案中缺失,你可以在大多数时间使用(至少我这样做):
但是,如果my_list包含其他容器(例如嵌套列表),则必须使用深度复制,如上文答案中从复制库中建议的那样 . 例如:
. Bonus :如果你不想复制元素使用(又称浅拷贝):
让我们理解解决方案#1和解决方案#2之间的区别
正如您所看到的,当我们不使用嵌套列表时,解决方案#1工作正常 . 让我们来看看当我们将解决方案#1应用于嵌套列表时会发生什么 .
与其他具有 variable and value 的语言不同,Python具有 name and object .
这个说法:
意味着给列表(对象)一个名称
a
,并且:只是给同一个对象
a
一个新名称b
,所以无论何时你用a
做什么,对象都会改变,因此b
会改变 .制作一个 really 副本的唯一方法是 create a new object 就像其他答案已经说过的那样 .
你可以看到更多关于这个here的信息 .