2008-06-22 17:44:14
Assignment
Assignment in Python often confuses newcomers to the language.
Why does this change the value of b?
>>> a = [1, 2, 3]
>>> b = a
>>> a[2] = 5
>>> b
[1, 2, 5]
But this does not?
>>> a = 10
>>> b = a
>>> a += 4
>>> b
10
Programmers coming from a background in C and C++ have a mental model of
variables as containers for values. This model works in those languages,
where variables correspond to memory locations and assignment copies a
value from one location to another. It doesn't work in Python.
To write correct Python programs you
have to have a different model for what variables are and how they
behave.
Python names are not containers for values, they are names (references) for
values. Assignment creates a name or binds a new value to a name.
Assignment never copies a value, it creates a new reference to the
same value. Think of the name as pointing to the value, or as a sticky note attached to the value.
The assignment b = a binds b to the same value to which a is bound. After the assignment, a and b refer to the same value. The difference between the two examples above is in what happens after the initial assignment.
In the first example, a and b are both bound to a list, which is mutable - it can be changed in place. The assignment
a[2] = 5
changes the value of the referenced list in place. a and b still point to the same list, and the value of the list has changed.
In the second case, a and b both refer to the integer 10. Integers are immutable - their value cannot change. The statement a += 4 computes the integer 14 and binds a to the new value. b is not changed - it is still bound to the value 10.
Note: Internally, most namespaces - where name look up happens - are implemented as dictionaries. The names are the keys; the dictionary values are the values.
Parameter passing
has the same semantics as assignment - it binds a name in the local
scope to the passed-in value. If you pass a list as a value and modify
the list, the caller will see the modification. This may or may not be
what you want; if you model parameter passing as copying values you will
be surprised by this behaviour. If you take this behaviour to mean that
values are passed "by reference", then you may be surprised when an
assignment to the parameter name in a function doesn't affect the
value at the caller. In other words, given this:
def f(x, y):
x = 3
y.append(1)
a = 5
b = []
f(a, b)
What are the current values of a and b? Without a correct model of
parameter passing you will not know.
A popular debate on comp.lang.python is over whether Python passes values by value or by reference. I'm no expert, but based on the meaning I learned for these term in C and C++, the answer is "neither".
If Python passed by value, then the function f() would not be able to change the value of b seen by the caller. (It does.)
If Python passed by reference, then f() would be able to change the value of a seen by the caller. (It can't.)
If you think of Python variables as references to values, then parameter passing can be seen as passing the reference by value. This is sometimes called "call by object reference" which seems as good a name as any.
|