[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