S
Steven D'Aprano
On Mon, 17 Jun 2013 08:17:48 +0300, Îίκος wrote:
[...]
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
[...]
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