Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/python/316.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Python 分配后列表意外更改。这是为什么?我如何预防?_Python_List_Reference_Copy_Clone - Fatal编程技术网

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已经提供了一个很好的答案,但我想我应该对各种方法进行速度比较:

  • 10.59秒(105.9µs/itn)-
  • 10.16秒(101.6µs/itn)-纯Python
    Copy()
    方法使用deepcopy复制类
  • 1.488秒(14.88µs/itn)-纯Python
    Copy()
    方法不复制类(仅dicts/list/tuple)
  • 0.325秒(3.25µs/itn)-
    旧列表中的项目:新列表。追加(项目)
  • 0.217秒(2.17µs/itn)[旧列表中的i代表i](a)
  • 0.186秒(1.86µs/itn)-
  • 0.075秒(0.75µs/itn)-
    列表(旧列表)
  • 0.053秒(0.53µs/itn)-
    new_list=[];新列表。扩展(旧列表)
  • 0.039秒(0.39µs/itn)-
    旧列表[:]
    ()
  • 所以最快的是列表切片。但是请注意,
    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))
    
  • 这是危险的,尤其是当你从一个你不信任的来源评估某件事情时
  • 如果您要复制的子元素没有可以被评估以重现等效元素的表示,那么这是不可靠的
  • 它的性能也比较差 在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
    >>> 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)