5

Newbie with a question, so please be gentle:

list = [1, 2, 3, 4, 5]
list2 = list

def fxn(list,list2):
    for number in list:
        print(number)
        print(list)
        list2.remove(number)
        print("after remove list is  ", list, " and list 2 is  ", list2)
    return list, list2

list, list2 = fxn(list, list2)
print("after fxn list is  ", list)
print("after fxn list2 is  ", list2)

This results in:

1
[1, 2, 3, 4, 5]
after remove list is   [2, 3, 4, 5]  and list 2 is   [2, 3, 4, 5]
3
[2, 3, 4, 5]
after remove list is   [2, 4, 5]  and list 2 is   [2, 4, 5]
5
[2, 4, 5]
after remove list is   [2, 4]  and list 2 is   [2, 4]
after fxn list is   [2, 4]
after fxn list2 is   [2, 4]

I don't understand why list is changing when I am only doing list2.remove(), not list.remove(). I'm not even sure what search terms to use to figure it out.

ShadowRanger
  • 143,180
  • 12
  • 188
  • 271
user1604149
  • 65
  • 1
  • 4
  • 1
    This used to happen to me all the time when I kept switching back and forth from scheme/lisp to python several years ago. In my opinion its easier to understand list objects that are immutable. – Adam Gent Aug 16 '12 at 19:20
  • 1
    Welcome to SO! Don't forget to accept an answer if any of the answers below showed you the solution to your problem. (you can accept an answer by clicking on the little check-mark next to it). – mgilson Aug 16 '12 at 19:53
  • @AdamGent: This is a good point. If you can write your code construct new lists instead of mutating them, then aliasing is never a problem. And you can use tuples instead of lists to make sure you're not accidentally mutating. That isn't always the right answer (either because of the efficiency characteristics of Python lists/tuples as effectively arrays, or because the conversion to an immutable algorithm isn't obvious and your time is limited), but it's always worth keeping in mind. – abarnert Aug 16 '12 at 21:11
  • @abarnert Your absolutely right. Lisp/Scheme are special in that there immutable list implementations (cons cells) are very fast and smart with memory. There somewhat analagous to how Java and Python handles strings. I once tried to switch out lists for arrays in CMUCL thinking that it would be a performance boost but I barely got any boost. The same is not true for Java, Scala, or Python where mutable lists are generally much faster. – Adam Gent Aug 16 '12 at 22:13
  • 1
    @user1604149 please click the check button to accept answers – Snakes and Coffee Aug 17 '12 at 04:55
  • Am I only allowed to check one answer? – user1604149 Sep 07 '12 at 20:19

2 Answers2

14

The reason this is happening can be found here:

mlist = [1,2,3,4,5]
mlist2 = mlist

the second statement "points" mlist2 to mlist (i.e., they both refer to the same list object) and any changes you make to one is reflected in the other.

To make a copy instead try this (using a slice operation):

mlist = [1,2,3,4,5]
mlist2 = mlist[:]

In case you are curious about slice notation, this SO question Python Lists(Slice method) will give you more background.

Finally, it is not a good idea to use list as an identifier as Python already uses this identifier for its own data structure (which is the reason I added the "m" in front of the variable names)

Community
  • 1
  • 1
Levon
  • 138,105
  • 33
  • 200
  • 191
  • 2
    Awesome! I do vaguely recall the reading about variables pointing to the same object and wondering what all the fuss was about. Now I know. – user1604149 Aug 16 '12 at 19:21
  • @user1604149 Yes, this is something definitely you need to be aware of and take into account. Glad my answer helped. – Levon Aug 16 '12 at 19:22
  • This is an interesting problem too because the list object is being modified as it is being iterated over which I think(?) results in undefined behavior... – mgilson Aug 16 '12 at 19:47
  • @mgilson That was my first thought when I saw this, but I think that at the core of this problem as OP posted it (the the bottom lines), is that two things are referring to the same object. – Levon Aug 16 '12 at 19:49
  • @Levon -- You're right. The two variables referencing the same object is definitely the underlying cause here. (Fix that and the second problem goes away too). I just thought it made the problem a little more interesting/convoluted :-) – mgilson Aug 16 '12 at 19:52
  • @user1604149 If this solved your problem please consider [accepting this answer by clicking the *checkmark*](http://meta.stackexchange.com/questions/5234/how-does-accepting-an-answer-work/5235#5235) next to the answer. This will award both of us some points and marks this problem as solved. – Levon Aug 18 '12 at 13:59
6

That's because both list and list2 are referring to the same list after you did the assignment list2=list.

Try this to see if they are referring to the same objects or different:

id(list)
id(list2)

An example:

>>> list = [1, 2, 3, 4, 5]
>>> list2 = list
>>> id(list)
140496700844944
>>> id(list2)
140496700844944
>>> list.remove(3)
>>> list
[1, 2, 4, 5]
>>> list2
[1, 2, 4, 5]

If you really want to create a duplicate copy of list such that list2 doesn't refer to the original list but a copy of the list, use the slice operator:

list2 = list[:]

An example:

>>> list
[1, 2, 4, 5]
>>> list2
[1, 2, 4, 5]
>>> list = [1, 2, 3, 4, 5]
>>> list2 = list[:]
>>> id(list)
140496701034792
>>> id(list2)
140496701034864
>>> list.remove(3)
>>> list
[1, 2, 4, 5]
>>> list2
[1, 2, 3, 4, 5]

Also, don't use list as a variable name, because originally, list refers to the type list, but by defining your own list variable, you are hiding the original list that refers to the type list. Example:

>>> list
<type 'list'>
>>> type(list)
<type 'type'>
>>> list = [1, 2, 3, 4, 5]
>>> list
[1, 2, 3, 4, 5]
>>> type(list)
<type 'list'>
Susam Pal
  • 32,765
  • 12
  • 81
  • 103