[Tutor] mutable types

Steven D'Aprano steve at pearwood.info
Tue Nov 2 15:15:54 CET 2010


John wrote:
> Hello, I thought the following should end with G[1] and G[0] returning
> 20. Why doesn't it?
> 
> In [69]: a = 10
> In [70]: G = {}
> In [71]: G[0] = [a]
> In [72]: G[1] = G[0]
> In [73]: a = 20
> In [74]: G[1]
> Out[74]: [10]
> In [75]: G[0]
> Out[75]: [10]

This doesn't actually have anything to do with the difference between 
mutable and immutable objects. Here's a simpler example to think about:

a = 10  # the name "a" is bound to the object 10
b = a  # the name "b" is bound to the same object "a" is bound to
a = 20  # the name "a" is bound to the object 20
b = ????

What value would you expect b to have?

The fact that you are using a mutable dictionary in your example is no 
different:

a = 10  # the name "a" is bound to the object 10
G = {}  # the name "G" is bound to an empty dictionary object
G[0] = [a]  # the dictionary bound to G has a mapping added,
   # the key 0 is mapped to a list containing the object bound to "a"

The important thing here is that the list [a] doesn't, and can't, know 
anything about the *name* "a". All it sees is the object that a is 
*currently* bound to. Once that object is placed in the list, there is 
no connection from the name "a" to the list, or visa versa. So later on, 
when you re-bind "a" to 20, that does nothing to the object 10, or the 
list that contains 10, or the dict that contains the list.

Around this time, many people start arguing about "call by value" and 
"call by reference" (or sometimes "pass by value" and "pass by 
reference"). If these terms mean nothing to you, congratulations! That's 
one less thing for you to care about, and you can stop reading now and 
go and do something productive. Arguments about calling conventions in 
programming languages often sink into long, unproductive flame wars.

Seriously. Stop reading and go do some programming.

Still here? Okay, don't say you weren't warned...

If you do know the terms "call by (pass by) reference" and "call by 
value", and you're thinking that Python is pass-by-reference for mutable 
objects, no, it's not. There is no way to get this behaviour in Python, 
regardless of whether you use mutable or immutable objects:

# this doesn't work
a = 1
b = 2
swap(a, b)  # call a function with by-reference parameters
assert a == 2  # fails
assert b == 1  # also fails

Can't be done from Python. There are alternatives that work just as 
well, such as the idiomatic:

a, b = b, a

but Python has no by-reference variables or arguments.

Now you're thinking that Python must be pass-by-value. If you're not, 
congratulations, you've just dodged a very common mistake. Python isn't 
pass-by-value *either*:

def func(alist):
     del alist[:]
     return alist

# this doesn't work either
a = [1,2,3]
b = func(a)  # pass-by-value automatically makes a copy of a
assert a == [1,2,3] and b = []  # fails

Python's evaluation strategy goes by various names, my favourite being 
pass-by-object. Another is call-by-sharing. Unfortunately, other 
languages fall into the trap of thinking that there are only two 
strategies, call-by-value and call-by-reference, and so they use one or 
the other of those names for the exact same behaviour that Python 
exhibits. Sigh.

http://en.wikipedia.org/wiki/Evaluation_strategy




-- 
Steven


More information about the Tutor mailing list