Search code examples
pythonlistclonemutable

How do I clone a list so that it doesn't change unexpectedly after assignment?


While using new_list = my_list, any modifications to new_list changes my_list every time. Why is this, and how can I clone or copy the list to prevent it? For example:

>>> my_list = [1, 2, 3]
>>> new_list = my_list
>>> new_list.append(4)
>>> my_list
[1, 2, 3, 4]

Solution

  • new_list = my_list doesn't actually create a second list. The assignment just copies the reference to the list, not the actual list, so both new_list and my_list refer to the same list after the assignment.

    To actually copy the list, you have several options:

    • You can use the built-in list.copy() method (available since Python 3.3):

      new_list = old_list.copy()
      
    • You can slice it:

      new_list = old_list[:]
      

      Alex Martelli's opinion (at least back in 2007) about this is, that it is a weird syntax and it does not make sense to use it ever. ;) (In his opinion, the next one is more readable).

    • You can use the built-in list() constructor:

      new_list = list(old_list)
      
    • You can use generic copy.copy():

      import copy
      new_list = copy.copy(old_list)
      

      This is a little slower than list() because it has to find out the datatype of old_list first.

    • If you need to copy the elements of the list as well, use generic copy.deepcopy():

      import copy
      new_list = copy.deepcopy(old_list)
      

      Obviously the slowest and most memory-needing method, but sometimes unavoidable. This operates recursively; it will handle any number of levels of nested lists (or other containers).

    Example:

    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}')
    

    Result:

    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)]