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' ?"
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")
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]]
>>>
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.
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[:]
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!
Also, you can do:
b = list(a)
This will work for any sequence, even those that don't support indexers and slices...
b = list(a)
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))]
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
To create a copy of a list do this:
b = a[:]
Source: Stackoverflow.com