Variables versus name bindings [Re: A certainl part of an if() structure never gets executed.]

Steven D'Aprano steve+comp.lang.python at pearwood.info
Mon Jun 17 02:51:56 EDT 2013


On Mon, 17 Jun 2013 08:17:48 +0300, Νίκος wrote:

[...]
>> The latter is false because the binding of "b" to the int 6 was broken
>> in order to bind b to the int 5.
> 
> Very surprising.
> a and b was *references* to the same memory address, it was like a
> memory address having 2 names to be addresses as.
> 
> b = a name we use to address some memory location, do we agree on that?
> 
> So, b = 6, must have changed the stored value of its mapped memory
> location, but given you example it seems its changing the mapping of b
> to some other memory address.
> 
> I don't follow its act of course.


Let me explain how variables work in some other languages, and how they 
work in Python. (And Ruby, and Java, and many others.)

In a language like Pascal, or C, the compiler keeps a table mapping 
variable names to fixed memory addresses, like this:

Variable  Address
========  =======
x         10234
y         10238
z         10242


Code like:

x := 42;
y := x + 1;


will get compiled into something that looks like this:

# Pseudo-code
STORE 42 AT ADDRESS 10234;
READ ADDRESS 10234;
STORE (LAST RESULT + 1) AT ADDRESS 10238;


The important thing is that memory addresses are known at compile time, 
and at least in general, variables cannot move around in memory. Another 
important thing is that assignment is copying:

x := y;

becomes:

READ ADDRESS 10234;
STORE (LAST RESULT) AT ADDRESS 10238;

which is equivalent to:

COPY ADDRESS 10234 TO ADDRESS 10238;

If, instead of an integer, x was an array of 1000 integers, all 1000 
integers would need to be copied.

Now, in languages like Python, Ruby, Java, and many others, there is no 
table of memory addresses. Instead, there is a namespace, which is an 
association between some name and some value:

global namespace:
    x --> 23
    y --> "hello world"


In Python, namespaces are *dicts*, just like those you create with {}.

Code like:

x = 42
y = x + 1


is treated as:

# Pseudocode
create the object 42
bind it to the name 'x'
look up the name 'x'
add 1 to it
bind it to the name 'y'


where "bind" means to change the association in the namespace:

global namespace:
    x --> 42
    y --> 43


One important thing is that binding does *not* make a copy of the object. 
Assignment is equally fast whether you have one int or a list or a 
million ints. So code like this:

x = y

results in both names 'x' and 'y' being associated to the same object. 
With ints, that's pretty boring, but for mutable objects like lists, it 
means that you get two names for the same list:

py> x = []
py> y = x
py> y.append("Surprise!")
py> x
['Surprise!']


This sort of behaviour is trivial in languages with name-binding 
semantics, like Python, but quite tricky in languages like Pascal. You 
have to explicitly work with pointers or other indirect memory access, 
leading to extra effort and the possibility of serious bugs.

Note also that because you aren't dealing with fixed memory addresses, 
objects are free to be moved in memory for better memory usage and less 
fragmentation. CPython doesn't do this, but PyPy does, and I expect that 
both Jython and IronPython probably do too. So long as the runtime 
environment can (somehow) track when objects are moved, it all works out 
fine.

Another difference is that in C-like languages, variables always have a 
value, even if it's not a useful value. As soon as the compiler decides 
that variable 'z' will be at address 10242, then 'z' has an implied value 
made up of whatever junk happens to be at that address. Some compilers 
will warn you if you try to use a variable without assigning to it first, 
since using junk you happen to find lying around in memory is usually a 
bad thing, but not all compilers.

In contrast, Python doesn't have this issue. If you haven't assigned to 
'z', then there is no such thing as 'z' in your namespace, and trying to 
use it will automatically give you an error:

py> x = z - 1
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name 'z' is not defined


There are other differences in regards to passing arguments to functions. 
I've written about that before:

http://mail.python.org/pipermail/tutor/2010-December/080505.html


-- 
Steven



More information about the Python-list mailing list