Python 分配后列表意外更改。这是为什么?我如何预防?
使用Python 分配后列表意外更改。这是为什么?我如何预防?,python,list,reference,copy,clone,Python,List,Reference,Copy,Clone,使用new_list=my_list时,对new_list的任何修改每次都会更改my_list。这是为什么?我如何克隆或复制列表以防止它?使用thing[:] >>> a = [1,2] >>> b = a[:] >>> a += [3] >>> a [1, 2, 3] >>> b [1, 2] >>> Python的习惯用法是newList=oldList[:]使用new\u lis
new_list=my_list
时,对new_list
的任何修改每次都会更改my_list
。这是为什么?我如何克隆或复制列表以防止它?使用thing[:]
>>> a = [1,2]
>>> b = a[:]
>>> a += [3]
>>> a
[1, 2, 3]
>>> b
[1, 2]
>>>
Python的习惯用法是
newList=oldList[:]
使用new\u list=my\u list
,实际上没有两个列表。作业只是将引用复制到列表,而不是实际列表,因此new\u list
和my\u list
在作业后都引用相同的列表
>>> a = [1,2]
>>> b = a[:]
>>> a += [3]
>>> a
[1, 2, 3]
>>> b
[1, 2]
>>>
要实际复制列表,您有多种可能:
- 您可以使用内置方法(从Python 3.3开始提供):
- 您可以将其切片:
(至少)关于这一点的看法是,这是一种奇怪的语法,使用它毫无意义(在他看来,下一个更具可读性)new_list = old_list[:]
- 您可以使用内置功能:
new_list = list(old_list)
- 您可以使用泛型:
这比
慢一点,因为它必须先找出list()
的数据类型old\u list
- 如果列表中包含对象,并且您也希望复制它们,请使用“通用”: 显然,这是最慢、最需要记忆的方法,但有时是不可避免的
import copy
class Foo(object):
def __init__(self, val):
self.val = val
def __repr__(self):
return 'Foo({!r})'.format(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\nlist.copy(): %r\nslice: %r\nlist(): %r\ncopy: %r\ndeepcopy: %r'
% (a, b, c, d, e, 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)]
Felix已经提供了一个很好的答案,但我想我应该对各种方法进行速度比较:
Copy()
方法使用deepcopy复制类Copy()
方法不复制类(仅dicts/list/tuple)旧列表中的项目:新列表。追加(项目)
列表(旧列表)
new_list=[];新列表。扩展(旧列表)
旧列表[:]
()copy.copy()
、list[:]
和list(list)
,与copy.deepcopy()
和python版本不同,它们不会复制列表中的任何列表、词典和类实例,因此如果原始列表发生更改,它们也会在复制的列表中发生更改,反之亦然
(如果有人感兴趣或想提出任何问题,请看以下脚本:)
我有一个Python 3.3+方法,它应该和切片一样快:
newlist = old_list.copy()
在Python中克隆或复制列表的选项有哪些?
在Python 3中,可以通过以下方式创建浅拷贝:
a_copy = a_list.copy()
在Python2和Python3中,您可以获得一个带有原始文件完整切片的浅拷贝:
a_copy = a_list[:]
a_copy = a_list[:]
解释
复制列表有两种语义方式。浅复制创建相同对象的新列表,深复制创建包含新等效对象的新列表
浅表副本
浅表副本仅复制列表本身,列表本身是对列表中对象的引用的容器。如果包含的对象本身是可变的,并且其中一个对象发生了更改,那么更改将反映在两个列表中
在Python2和Python3中有不同的实现方法。Python2方法也可以在Python3中使用
Python 2
在Python 2中,制作列表的浅拷贝的惯用方法是使用原始列表的完整片段:
a_copy = a_list[:]
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
在Python3中,list获得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
>>> 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
创建另一个指针不会创建副本
使用新列表=我的列表然后在每次我的列表更改时修改新列表。为什么会这样?
my_list
只是指向内存中实际列表的名称。当你说new_list=my_list
你不是在复制,你只是在添加另一个指向内存中原始列表的名称。当我们复制列表时,可能会遇到类似的问题
>>> l = [[], [], []]
>>> l_copy = l[:]
>>> l_copy
[[], [], []]
>>> l_copy[0].append('foo')
>>> l_copy
[['foo'], [], []]
>>> l
[['foo'], [], []]
列表只是指向内容的指针数组,因此浅层副本只是复制指针,因此有两个不同的列表,但它们具有相同的内容。要复制内容,您需要一份深度副本
深拷贝
作出决定:
为了演示这如何允许我们创建新的子列表:
>>> import copy
>>> l
[['foo'], [], []]
>>> l_deep_copy = copy.deepcopy(l)
>>> l_deep_copy[0].pop()
'foo'
>>> l_deep_copy
[[], [], []]
>>> l
[['foo'], [], []]
因此,我们看到深度复制的列表与原始列表完全不同。您可以使用自己的函数,但不要这样做。使用标准库的deepcopy函数很可能会产生错误,否则就不会产生错误
from copy import deepcopy
deep = deepcopy(list_2)
不要使用eval
您可能会看到这被用作deepcopy的一种方式,但不要这样做:
problematic_deep_copy = eval(repr(a_list))
>>> 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
>>> 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
>>> 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\u list
引用的对象,并将其绑定到new\u list
。无论您使用哪个名称,仍然只有一个列表,因此在将其称为my_list
时所做的更改将在将其称为new_list
时保持不变。此问题的其他每个答案都提供了创建新对象以绑定到新列表
的不同方法
列表中的每个元素都像一个nam
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)
METHOD TIME TAKEN
b = [*a] 2.75180600000021
b = a * 1 3.50215399999990
b = a[:] 3.78278899999986 # Python 2 winner (see above)
b = a.copy() 4.20556500000020 # Python 3 "slice equivalent" (see above)
b = []; b.extend(a) 4.68069800000012
b = a[0:len(a)] 6.84498999999959
*b, = a 7.54031799999984
b = list(a) 7.75815899999997
b = [i for i in a] 18.4886440000000
b = copy.copy(a) 18.8254879999999
b = []
for item in a:
b.append(item) 35.4729199999997
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", 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\t", timeit.timeit(stmt='b = a[0:len(a)]', setup=setup, number=COUNT))
print("*b, = a\t\t\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 = []; for 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))
print("b = [*a]\t\t", timeit.timeit(stmt='b = [*a]', setup=setup, number=COUNT))
print("b = a * 1\t\t", timeit.timeit(stmt='b = a * 1', setup=setup, number=COUNT))
new_list = my_list[:]
new_list = my_list * 1 # Solution 1 when you are not using nested lists
import copy
new_list = copy.deepcopy(my_list) # Solution 2 when you are using nested lists
new_list = my_list[:]
>>> 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])
>>> 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
print(id(copy))
print(id(list_1))
4329485320
4329485320
copy[0] = "modify"
print(copy)
print(list_1)
['modify', '98']
['modify', '98']
copy_1 = list_1[:]
print(id(copy_1))
print(id(list_1))
4338792136
4338791432
copy_1[0] = "modify"
print(list_1)
print(copy_1)
['01', '98']
['modify', '98']
copy_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']]
copy_2 = list_2[:]
print(id(copy_2[0]))
print(id(list_2[0]))
4329485832
4329485832
from copy import deepcopy
deep = deepcopy(list_2)
print(id((list_2)), id(deep))
4322146056 4322148040
print(id(deep[0]))
print(id(list_2[0]))
4322145992
4322145800
deep[0][1] = "modify"
print(list_2, deep)
[['01', '98']] [['01', 'modify']]
old_list = [1, 2, 3]
new_list = [*old_list]
new_list.append(4)
old_list == [1, 2, 3]
new_list == [1, 2, 3, 4]
x = [random.random() for _ in range(1000)]
%timeit a = list(x)
%timeit a = x.copy()
%timeit a = x[:]
%timeit a = [*x]
#: 2.47 µs ± 38.1 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)
#: 2.47 µs ± 54.6 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)
#: 2.39 µs ± 58.2 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)
#: 2.22 µs ± 43.2 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)
import copy
class MyList(list):
pass
lst = MyList([1,2,3])
lst.name = 'custom list'
d = {
'original': lst,
'slicecopy' : lst[:],
'lstcopy' : lst.copy(),
'copycopy': copy.copy(lst),
'deepcopy': copy.deepcopy(lst)
}
for k,v in d.items():
print('lst: {}'.format(k), end=', ')
try:
name = v.name
except AttributeError:
name = 'NA'
print('name: {}'.format(name))
lst: original, name: custom list
lst: slicecopy, name: NA
lst: lstcopy, name: NA
lst: copycopy, name: custom list
lst: deepcopy, name: custom list
def deepcopy(x):
immutables = (str, int, bool, float)
mutables = (list, dict, tuple)
if isinstance(x, immutables):
return x
elif isinstance(x, mutables):
if isinstance(x, tuple):
return tuple(deepcopy(list(x)))
elif isinstance(x, list):
return [deepcopy(y) for y in x]
elif isinstance(x, dict):
values = [deepcopy(y) for y in list(x.values())]
keys = list(x.keys())
return dict(zip(keys, values))
[x for x in _list]
[deepcopy_list(x) for x in _list]
def deepcopy_list(x):
if isinstance(x, (str, bool, float, int)):
return x
else:
return [deepcopy_list(y) for y in x]
>>> b = a = ['hell', 'word']
>>> c = ['hell', 'word']
>>> id(a), id(b), id(c)
(4424020872, 4424020872, 4423979272)
| |
-----------
>>> id(a[0]), id(b[0]), id(c[0])
(4424018328, 4424018328, 4424018328) # all referring to same 'hell'
| | |
-----------------------
>>> id(a[0][0]), id(b[0][0]), id(c[0][0])
(4422785208, 4422785208, 4422785208) # all referring to same 'h'
| | |
-----------------------
>>> a[0] += 'o'
>>> a,b,c
(['hello', 'word'], ['hello', 'word'], ['hell', 'word']) # b changed too
>>> id(a[0]), id(b[0]), id(c[0])
(4424018384, 4424018384, 4424018328) # augmented assignment changed a[0],b[0]
| |
-----------
>>> b = a = ['hell', 'word']
>>> id(a[0]), id(b[0]), id(c[0])
(4424018328, 4424018328, 4424018328) # the same hell
| | |
-----------------------
>>> import gc
>>> gc.get_referrers(a[0])
[['hell', 'word'], ['hell', 'word']] # one copy belong to a,b, the another for c
>>> gc.get_referrers(('hell'))
[['hell', 'word'], ['hell', 'word'], ('hell', None)] # ('hello', None)
list1 = ['apples','bananas','pineapples']
list2 = list1
from copy import deepcopy
a = [ [ list(range(1, 3)) for i in range(3) ] ]
b = deepcopy(a)
b[0][1]=[3]
print('Deep:')
print(a)
print(b)
print('-----------------------------')
a = [ [ list(range(1, 3)) for i in range(3) ] ]
b = a*1
b[0][1]=[3]
print('*1:')
print(a)
print(b)
print('-----------------------------')
a = [ [ list(range(1, 3)) for i in range(3) ] ]
b = a[:]
b[0][1]=[3]
print('Vector copy:')
print(a)
print(b)
print('-----------------------------')
a = [ [ list(range(1, 3)) for i in range(3) ] ]
b = list(a)
b[0][1]=[3]
print('List copy:')
print(a)
print(b)
print('-----------------------------')
a = [ [ list(range(1, 3)) for i in range(3) ] ]
b = a.copy()
b[0][1]=[3]
print('.copy():')
print(a)
print(b)
print('-----------------------------')
a = [ [ list(range(1, 3)) for i in range(3) ] ]
b = a
b[0][1]=[3]
print('Shallow:')
print(a)
print(b)
print('-----------------------------')
Deep:
[[[1, 2], [1, 2], [1, 2]]]
[[[1, 2], [3], [1, 2]]]
-----------------------------
*1:
[[[1, 2], [3], [1, 2]]]
[[[1, 2], [3], [1, 2]]]
-----------------------------
Vector copy:
[[[1, 2], [3], [1, 2]]]
[[[1, 2], [3], [1, 2]]]
-----------------------------
List copy:
[[[1, 2], [3], [1, 2]]]
[[[1, 2], [3], [1, 2]]]
-----------------------------
.copy():
[[[1, 2], [3], [1, 2]]]
[[[1, 2], [3], [1, 2]]]
-----------------------------
Shallow:
[[[1, 2], [3], [1, 2]]]
[[[1, 2], [3], [1, 2]]]
-----------------------------
int my_list[] = [1,2,3,4];
int *new_list;
new_list = my_list;
import copy
new_list = copy.deepcopy(my_list)
number=[1,2,3,4,5,6] #Original list
another=[] #another empty list
for a in number: #here I am declaring variable (a) as an item in the list (number)
another.append(a) #here we are adding the items of list (number) to list (another)
print(another)
>>> [1,2,3,4,5,6]
l = [1,2,3]
l2 = l + []
print(l,l2)
l[0] = 'a'
print(l,l2)