python list by value not by reference

251,515

Solution 1

You cannot pass anything by value in Python. If you want to make a copy of a, you can do so explicitly, as described in the official Python FAQ:

b = a[:]

Solution 2

To copy a list you can use list(a) or a[:]. In both cases a new object is created.
These two methods, however, have limitations with collections of mutable objects as inner objects keep their references intact:

>>> a = [[1,2],[3],[4]]

>>> b = a[:]
>>> c = list(a)

>>> c[0].append(9)

>>> a
[[1, 2, 9], [3], [4]]
>>> c
[[1, 2, 9], [3], [4]]
>>> b
[[1, 2, 9], [3], [4]]
>>> 

If you want a full copy of your objects you need copy.deepcopy

>>> from copy import deepcopy
>>> a = [[1,2],[3],[4]]

>>> b = a[:]
>>> c = deepcopy(a)

>>> c[0].append(9)

>>> a
[[1, 2], [3], [4]]
>>> b
[[1, 2], [3], [4]]
>>> c
[[1, 2, 9], [3], [4]]
>>> 

Solution 3

In terms of performance my favorite answer would be:

b.extend(a)

Check how the related alternatives compare with each other in terms of performance:

In [1]: import timeit

In [2]: timeit.timeit('b.extend(a)', setup='b=[];a=range(0,10)', number=100000000)
Out[2]: 9.623248100280762

In [3]: timeit.timeit('b = a[:]', setup='b=[];a=range(0,10)', number=100000000)
Out[3]: 10.84756088256836

In [4]: timeit.timeit('b = list(a)', setup='b=[];a=range(0,10)', number=100000000)
Out[4]: 21.46313500404358

In [5]: timeit.timeit('b = [elem for elem in a]', setup='b=[];a=range(0,10)', number=100000000)
Out[5]: 66.99795293807983

In [6]: timeit.timeit('for elem in a: b.append(elem)', setup='b=[];a=range(0,10)', number=100000000)
Out[6]: 67.9775960445404

In [7]: timeit.timeit('b = deepcopy(a)', setup='from copy import deepcopy; b=[];a=range(0,10)', number=100000000)
Out[7]: 1216.1108016967773

Solution 4

Also, you can do:

b = list(a)

This will work for any sequence, even those that don't support indexers and slices...

Solution 5

If you want to copy a one-dimensional list, use

b = a[:]

However, if a is a 2-dimensional list, this is not going to work for you. That is, any changes in a will also be reflected in b. In that case, use

b = [[a[x][y] for y in range(len(a[0]))] for x in range(len(a))]
Share:
251,515

Related videos on Youtube

d.putto
Author by

d.putto

Updated on March 19, 2021

Comments

  • d.putto
    d.putto about 3 years

    Let's take an example

    a=['help', 'copyright', 'credits', 'license']
    b=a
    b.append('XYZ')
    b
    ['help', 'copyright', 'credits', 'license', 'XYZ']
    a
    ['help', 'copyright', 'credits', 'license', 'XYZ']
    

    I wanted to append value in list 'b' but the value of list 'a' have also changed.
    I think I have little idea why its like this (python passes lists by reference).
    My question is "how can I pass it by value so that appending 'b' does't change values in 'a' ?"

    • Rezaul Karim Shaon
      Rezaul Karim Shaon about 2 years
      b = a.copy() , this way it will work.
  • Kyle Pittman
    Kyle Pittman about 8 years
    Thank you for bringing performance into the discussion, this helped me to make a decision about which method to use.
  • AsheKetchum
    AsheKetchum about 7 years
    what is the difference between a regular copy and a deep copy? Why does what happens above happen? I think I have a general understanding, it seems to be like the same problem encountered by the op at the second layer. How does it work internally?
  • Felipe
    Felipe about 7 years
    Great answer, Jordan! Thanks!!! Do you know the reason for this?
  • Mannix
    Mannix almost 7 years
    Doesn't work for me. Any changes I make to b are also seen in a.
  • phihag
    phihag almost 7 years
    @Mannix Can you post the full code you have which shows the problem (i.e. an assertion should fail) in a new question? Most likely, you're not modifying the list itself, but its elements. Create a deep copy if you want a new list whose elements are copies as well.
  • Artur
    Artur almost 7 years
    I've just found your answer, thank you for this high quality reply! When discussing Python often performance isn't considered and for huge data sets it makes difference.
  • Eran Yogev
    Eran Yogev over 6 years
    I like this answer, however, it doesn't relate to the values of the list. As Jordan Pagni mentioned, if your list is multidimensional, as in lists within lists (and more), then the only solution that will work is the one that takes the longest time: b = deepcopy(a)
  • Kyr
    Kyr over 6 years
    @EranYogev, I agree. If you want to achieve performance though you have to have some knowledge about your list's contents. :(
  • Mr. Deathless
    Mr. Deathless over 6 years
    Test case for extend() call is not comparable to others. To use extend() you must create an array first while other constructs will create an array for you. So you effectively giving extend() an advantage by skipping initialization of the list object. To correct the test move b = [] from setup to a statement under the test, like b = []; b.extend(a). This will change results in favor of second case which uses slicing to create a copy.
  • AruniRC
    AruniRC about 6 years
    Great answer, especially as it mentions both the case where the initial solution will fail (nested objects, a list of other objects) and the solution ( deepcopy() ).
  • Pythoner
    Pythoner almost 6 years
    However, if a is a 2-dimensional list, this is not going to work
  • Pythoner
    Pythoner almost 6 years
    For 2D arrays it's possible use map function: old_array = [[2, 3], [4, 5]] # python2.* new_array = map(list, old_array) # python3.* new_array = list(map(list, old_array))
  • phihag
    phihag almost 6 years
    @Pythoner The code you describe works for 2D lists, not arrays. copy.deepcopy(something) works for both. But then again, if your list is 2D - or any data structure but a simple list - then you have a different question than the one here.
  • scherm
    scherm over 5 years
    Why does b=list(a) take twice as long as b=a[:] ?
  • Endyd
    Endyd almost 5 years
    Just a technicality, but python variables are not really pointers. It would be more accurate to say when you do b = a you create another reference to the list object referenced by a.
  • Endyd
    Endyd almost 5 years
    This is not better than b = a[:]
  • Dmitry Polovinkin
    Dmitry Polovinkin over 2 years
    But know that in that case if you have a list inside of a, and if you change it in b, it will also change in a. But integers or strings will not be changed. That's what is meant by multidimensional lists mentions
  • Dmitry Polovinkin
    Dmitry Polovinkin over 2 years
    note that this approach will also not fully work with multidimensional lists - so if you have a list in original list, it will change everywhere if changed in one copy
  • Dmitry Polovinkin
    Dmitry Polovinkin over 2 years
    not really the case that any changes will be reflected - only if list inside the original list is changed it will reflect on the copy. Other data changes will not be reflected on other copy, so strings or integers are safe to change