swcarpentry / python-novice-gapminder

Plotting and Programming in Python
http://swcarpentry.github.io/python-novice-gapminder/
Other
164 stars 432 forks source link

Mutable vs. immutable objects discussion in Lesson 11 #360

Open arnsong opened 5 years ago

arnsong commented 5 years ago

In lesson 11, which is the lesson where lists are introduced, the concept of object mutability is introduced. A character string is given as an example of an immutable object and lists as a mutable object. It seems that this might be a natural place to mention how python treats assignment to mutable objects because a novice would likely encounter the side effects associated with the assumption that mutable object assignment copies the object reference not the object itself.

This is alluded to in the copying exercise, but an explained example may help a learner with this subtle but important python concept. I know when I was first learning python that this caused me all kinds of grief because I didn't know about this.

For example, which might add a couple of minutes to this lesson:

myList = ['apple', 'banana', 'cherry', 'date']
print(myList)

# Since lists are mutable objects I can modify my variable, myList, by replacing
# the apple with an orange
myList[0] = 'orange'
print(myList)

# I can assign another variable to the list pointed to by myList
yourList = myList

# But be careful, yourList and myList point to the same object, so if you change something in myList, you're changing the same list assigned to yourList
myList[2] = 'pineapple'

# The outputs will be identical
print(myList)
print(yourList)

# If you're trying to make a copy, you need to do this explicitly for mutable objects in python
import copy

theirList = copy.copy(myList)
theirList = myList[:]

myList[3] = 'grape'

# Now the lists are distinct
print(myList)
print(theirList)
vahtras commented 5 years ago

Thank you for your suggestion. I believe that the exercise "Copying (or not)" in Lesson 11 already covers some of this. Please allow me to clarify one thing

theirList = copy.copy(myList)
theirList = myList[:]

are two independent copy operations. The second line creates a new list and makes the name theirList refer to that object, so that the result of the copy module function is lost.

>>> theirList = copy.copy(myList)
>>> print(id(theirList))
140475935149320
>>> theirList = myList[:]
>>> print(id(theirList))
140475935284552

Apart from that, your discussion is valid also without referring to the copy module. On the other hand it gets more complicated if the list contains other lists and we must introduce concepts like shallow and deep copy, where the copy module becomes useful. Do we want to go there at this level? Regards, Olav

arnsong commented 5 years ago

Hi Olav,

Oops, you're right. That was a mistake in presentation. Those two lines were meant to show that the operations were equivalent. Also, it's definitely better to leave the copy module out of the discussion as you suggest.

I guess I'm not convinced that the exercise is sufficient for what I consider a subtle and important point and perhaps a brief discussion is warranted. I concede that I may be putting too much emphasis on this as a result of my personal trauma. =)

Best, Arnold