使用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
其他回答
已经有很多答案告诉你如何制作一个正确的副本,但没有一个答案说明为什么你的原始“副本”失败了。
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)
有关复制中的角盒的详细信息,请参阅文档。
菲利克斯已经给出了一个很好的答案,但我想我应该对各种方法进行速度比较:
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
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)]
还有另一种方法可以复制一个直到现在才列出的列表:添加一个空列表:l2=l+[]。
我用Python 3.8测试了它:
l = [1,2,3]
l2 = l + []
print(l,l2)
l[0] = 'a'
print(l,l2)
这不是最好的答案,但它奏效了。
new_list = my_list[:]
new_list=我的列表
试着理解这一点。假设my_list位于堆内存中的位置X,即my_list指向X。现在,通过指定new_list=my_list,可以让new_list指向X。这就是所谓的浅拷贝。
现在,如果指定new_list=my_list[:],则只需将my_list的每个对象复制到new_list。这就是所谓的深度复制。
您可以通过以下其他方式完成此操作:
new_list=列表(old_list)导入副本new_list=复制.depcopy(old_list)
推荐文章
- 将Pandas或Numpy Nan替换为None以用于MysqlDB
- 使用pandas对同一列进行多个聚合
- 使用Python解析HTML
- django MultiValueDictKeyError错误,我如何处理它
- 如何在for循环期间修改列表条目?
- 我如何在Django中创建一个鼻涕虫?
- 数组与列表的性能
- 没有名为'django.core.urlresolvers'的模块
- 蟒蛇导出环境文件
- Django - makemigrations -未检测到任何更改
- SQLAlchemy:引擎、连接和会话差异
- 在Python Pandas中删除多个列中的所有重复行
- 更改pandas DataFrame中的特定列名
- 将Pandas多索引转换为列
- 熊猫在每组中获得最高的n个记录