List identified with another, using new elements in the loop

advertisements

This question already has an answer here:

  • python list by value not by reference 11 answers

I have a list named MyList. I want to copy the list to a new one, then add items to the new one, so I do:

MySecondList=MyList

for item in MyList:
    if item==2:
        MySecondList.append(item)

The problem I am having is that the items will be added also to MyList, and as a matter of fact the loop keeps going through MyList new items too!!

Is that normal? why does it happen? shouldnt the iteration use only the original list MyList for it, instead of dinamically increase with the items I add to other list?


Yes, it is normal as lists are mutable in python and this operation:

MySecondList = MyList

simply creates a new reference to the same list object and list.append modifies the same object in-place.(other operations like +=, list.extend, list.pop etc also modify the list in-place)

You can use a shallow copy here:

MySecondList = MyList[:]

Demo:

>>> from sys import getrefcount
>>> lis = [1,2,3]
>>> foo = lis       #creates a new reference to the same object [1,2,3]
>>> lis is foo
True
>>> getrefcount(lis) #number of references to the same object
3                    #foo , lis and shell itself

#you can modify the list [1,2,3] from any of it's references
>>> foo.append(4)
>>> lis.append(5)
>>> foo,lis
([1, 2, 3, 4, 5], [1, 2, 3, 4, 5])

>>> lis = [1,2,3]
>>> foo = lis[:]    #assigns a shallow copy of lis to foo
>>> foo is lis
False
>>> getrefcount(lis) #still 2(lis + shell_, as foo points to a different object
2

#different results here
>>> foo.append(4)
>>> lis.append(5)
>>> foo, lis
([1, 2, 3, 4], [1, 2, 3, 5])

For a lists of lists(or list of mutable objects) a shallow copy is not enough as the inner lists(or objects) are just new references to the same object:

>>> lis = [[1,2,3],[4,5,6]]
>>> foo = lis[:]
>>> foo is lis              #lis and foo are different
False

>>> [id(x) for x in lis]    #but inner lists are still same
[3056076428L, 3056076716L]
>>> [id(x) for x in foo]    #same IDs of inner lists, i.e foo[0] is lis[0] == True
[3056076428L, 3056076716L]
>>> foo[0][0] = 100         # modifying one will affect the other as well
>>> lis[0],foo[0]
([100, 2, 3], [100, 2, 3])

For such cases use copy.deepcopy:

>>> from copy import deepcopy
>>> lis = [[1,2,3],[4,5,6]]
>>> foo = deepcopy(lis)