Newcomer question wrt variable scope/namespaces

Magnus Lycka lycka at carmen.se
Wed Jan 18 06:09:23 EST 2006


Florian Daniel Otel wrote:
> Do I understand it correctly
> that actually the rule has to be refined as pertaining  to the (so
> called) "immutable" types (like e.g.  integers, tuples/strings)
> whereas lists and dictionaries are "mutable" types and the said
> scoping rule does not apply ?

No! The only difference between mutable and immutable
objects are that the value of mutable objects can be
changed after object creation.

The thing you need to understand is how variables, objects
and assignments work in Python! We are always dealing with
references to objects in Python.

In e.g. C, the code "int c; c=1; c=2;" roughly means:
Make a place for an integer in memory, and let's refer
to that place as "c". Then place the value 1 in c.
Finally, replace the value 1 with the value 2 in c.

This isn't at all how Python works. I think of C variables
as differently shaped (typed) boxes with a label glued onto
the side, but I think of Python variables as a tag or label
tied to a string (string as in thin rope, not text). The
other end of that string is tied to an object in a big,
shared storage room.

C assignments mean that you make a copy of some data and
put in a box, discarding whatever was previously in that
box. Python assignment means that you untie a string from
an object and tie it to another (possibly newly created)
object. When an object no longer has any strings attached,
it's (usually) considered to be garbage.

So, in Python, "c=1; c=2" means that you have a variable
(or name) called c in the current namespace. That's not a
place for some kind of object as in C, it's just a name
that can be bound to some object in that shared storage
room that we nerds call the heap. First, c will be bound
to, or refer to, an integer object on the heap containing
the value 1. Then c will be rebound to another object, 2.

In C, you have your different boxes neatly stacked in
various namespaces. In Python the boxes are all in one place,
but your labels are neatly organized in various namespaces.

You can make "strings" like this in C too, they are called
pointers, but it's not automatic as in Python, and C
pointers are much more difficult and error prone.

So, returning to your question, scoping rules are just
the same. The issue with mutable object are that they can
be mutated. Different variables in different namespaces
can refer to the same object. E.g.

 >>> l = [1,2]
 >>> def addToList1(aList, value):
...     aList.append(value)
...     return aList
...
 >>> def addToList2(aList, value):
...     newList = aList + [value]
...     return newList
...
 >>> print l
[1, 2]
 >>> print id(l)
182894638800
 >>> l1=addToList1(l,3)
 >>> print l, l1
[1, 2, 3] [1, 2, 3]
 >>> print id(l), id(l1)
182894638800 182894638800
 >>> l2=addToList2(l,4)
 >>> print l, l2
[1, 2, 3] [1, 2, 3, 4]
 >>> print id(l), id(l2)
182894638800 182894638736

You see? Calling addToList1 caused the list object that was
passed in to be modified. This modification is obviously
seen by all variables that are bound to that object, whatever
namespace they exist in.

Calling addToList2 doesn't cause any modification of the
objects that the parameters are bound to. Instead a new object
is created.

With immutable objects, you can't possibly do something along
the lines of addToList1. This means that if x is bound to an
immutable object, x will always have the same value before and
after a call to f(x), whatever code function f contains. If
x is mutable, you have no such guarantees. It depends on f...

It's not only variables that can be bound to objects. E.g:
 >>> l1 = []
 >>> l2 = [l1]
 >>> t1 = [l1]
 >>> print l1, l2, t1
[] [[]] [[]]
 >>> t1[0].append(1)
 >>> print l1, l2, t1
[1] [[1]] [[1]]

l1, position 0 in l2 and position 0 in t1 are all bound to the
same object, an initially empty list.





More information about the Python-list mailing list