205

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' ?"

Benjamin Gruenbaum
  • 270,886
  • 87
  • 504
  • 504
d.putto
  • 7,185
  • 11
  • 39
  • 45

11 Answers11

284

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[:]
user2357112
  • 260,549
  • 28
  • 431
  • 505
phihag
  • 278,196
  • 72
  • 453
  • 469
  • 28
    Doesn't work for me. Any changes I make to `b` are also seen in `a`. – Mannix May 28 '17 at 19:50
  • 1
    @Mannix Can you post the [full code](https://stackoverflow.com/help/mcve) 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](https://docs.python.org/dev/library/copy.html#copy.deepcopy) if you want a new list whose elements are copies as well. – phihag May 28 '17 at 20:28
  • 10
    However, if a is a 2-dimensional list, this is not going to work – Pythoner Jun 25 '18 at 14:55
  • 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)) – Pythoner Jun 25 '18 at 14:59
  • @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. – phihag Jun 25 '18 at 16:54
  • 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 Nov 26 '21 at 08:52
164

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]]
>>> 
joaquin
  • 82,968
  • 29
  • 138
  • 152
  • 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? – AsheKetchum Feb 21 '17 at 15:59
42

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
Kyr
  • 5,383
  • 2
  • 27
  • 22
  • 1
    Thank you for bringing performance into the discussion, this helped me to make a decision about which method to use. – Kyle Pittman Apr 14 '16 at 14:10
  • 1
    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. – Artur Aug 01 '17 at 08:06
  • 1
    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) – Eran Yogev Nov 13 '17 at 12:06
  • @EranYogev, I agree. If you want to achieve performance though you have to have some knowledge about your list's contents. :( – Kyr Nov 13 '17 at 12:11
  • 8
    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. – Mr. Deathless Dec 27 '17 at 10:07
  • 1
    Why does `b=list(a)` take twice as long as `b=a[:]` ? – scherm Dec 06 '18 at 14:31
19

Also, you can do:

b = list(a)

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

Daren Thomas
  • 67,947
  • 40
  • 154
  • 200
  • 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 Nov 26 '21 at 08:59
17

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))]
Rohan Saxena
  • 3,133
  • 2
  • 16
  • 34
  • 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 – Dmitry Polovinkin Nov 26 '21 at 09:03
16

As mentioned by phihag in his answer,

b = a[:]

will work for your case since slicing a list creates a new memory id of the list (meaning you are no longer referencing the same object in your memory and the changes you make to one will not be reflected in the other.)

However, there is a slight problem. If your list is multidimensional, as in lists within lists, simply slicing will not solve this problem. Changes made in the higher dimensions, i.e. the lists within the original list, will be shared between the two.

Do not fret, there is a solution. The module copy has a nifty copying technique that takes care of this issue.

from copy import deepcopy

b = deepcopy(a)

will copy a list with a new memory id no matter how many levels of lists it contains!

Jordan Pagni
  • 454
  • 3
  • 11
  • Great answer, Jordan! Thanks!!! Do you know the reason for this? – Felipe Apr 24 '17 at 15:33
  • 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() ). – AruniRC May 16 '18 at 15:04
6

When you do b = a you simply create another pointer to the same memory of a, that's why when you append to b , a changes too.

You need to create copy of a and that's done like this:

b = a[:]
Gil.I
  • 895
  • 12
  • 23
  • 1
    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 Jul 30 '19 at 18:15
5

To create a copy of a list do this:

b = a[:]
Rob Wouters
  • 15,797
  • 3
  • 42
  • 36
3

I found that we can use extend() to implement the function of copy()

a=['help', 'copyright', 'credits', 'license']
b = []
b.extend(a)
b.append("XYZ") 
Andy
  • 49,085
  • 60
  • 166
  • 233
user3754245
  • 61
  • 1
  • 1
  • 4
2

I would recommend the following solution:

b = []
b[:] = a

This will copy all the elements from a to b. The copy will be value copy, not reference copy.

VHS
  • 9,534
  • 3
  • 19
  • 43
1

b = list(a)

See http://henry.precheur.org/python/copy_list.

BenH
  • 2,100
  • 2
  • 22
  • 33