[Tutor] <var:data> value changes

denis denis.spir at free.fr
Thu May 13 10:04:36 EDT 2004


Hello,

Some comments and corrections about the previous message (thanks to Magnus &
Danny) pointed Python's feature called interning: the first aspect of this
feature is that integers in range(-5,100) (I didn't know about negative
ones) are generated and stored as data by python; the second is that they
won't
be duplicated, but used 'in place' instead, each time the value is needed:

>>> x=1; y=1 ; x is y
True
>>> x = -1; y = -1 ; x is y
True

This means that x and y are at the same memory location, in other words 'x'
and 'y' are aliases:

>>> id(x); id(y)
7678960
7678960

Now, let's (try and) check this does not with out-of-range (sic!) values:

>>> x=100; y=100 ; x is y
True
>>> x=1000; y=1000 ; x is y
True

What's up ? (I've really been surprised!) A more careful trial:

>>> x=1000
>>> y=1000
>>> x is y
False
>>> x=100
>>> y=100
>>> x is y
False
>>> x=99
>>> y=99
>>> x is y
True

This difference of result shows that two 'equivalent' syntactic forms aren't
processed the same way. I guess that, on optimisation ground, a short-term
interning is done for multi-assignment lines (what about "x,y = a,a"?). How
long does it last ? Is it further available ?

>>> x=100; y=100 ; x is y
True
>>> z=100 ; x is z; y is z
False
False

Now, what about strings ?

>>> x='a'
>>> y='a'
>>> x is y
True
>>> x='1'
>>> y='1'
>>> x is y
True
>>> x='Freude, schöner Götterfunken,..."
SyntaxError: EOL while scanning single-quoted string    # ¤$%#~!!!
>>> x='Freude, schöner Götterfunken,...'
>>> y='Freude, schöner Götterfunken,...'
>>> x is y
False
>>> x='Freude, schöner Götterfunken,...'; y='Freude, schöner
Götterfunken,...' ; x is y  # this was one single line in IDLE
True

As expected, basic interning is available for small strings (what are the
criteria?); and the short-term interning form works too.
What about sequences ?

>>> x=('a')
>>> y=('a')
>>> x is y
True

>>> x=['a']
>>> y=['a']
>>> x is y
False
>>> x=['a']; y=['a'] ; x is y
False
>>> x=[]; y=[] ; x is y
False
>>> x=[True]; y=[True] ; x is y
False

Very probable conclusion : yes for immutables, no for mutables. It doesn't
even work for empty lists or built-in constants.



Now, I really want to explore this difference deeper. First, let's enlarge x
a bit:

>>> x+=[False]
>>> x
[True, False]
>>> x+='a'
>>> x
[True, False, 'a']
>>> a='ciao!'
>>> x+=[a]
>>> x
[True, False, 'a', 'ciao!']
>>> x+=1

Traceback (most recent call last):
  File "<pyshell#69>", line 1, in -toplevel-
    x+=1
TypeError: argument to += must be iterable

I typed the last line just for fun, but I am surprised that python accepts
x+='a', as
'a' is no list, and refuses x+=1 (?). I would expect to be obliged to write
x.append(item) for both strings and numbers, as both are immutable --but
strings are sequences (what about tuples?). And with a dictionary ?

>>> d={}
>>> x+=d
>>> x
[True, False, 'a', 'ciao!']    # no success ;-(
>>> x+=[d]
>>> x
[True, False, 'a', 'ciao!', {}]    # :-)
>>> d={1:'un', 2:'deux'}
>>> x+=d
>>> x
[True, False, 'a', 'ciao!', {}, 1, 2]    # the keys, only!
>>> x+= [d]
>>> x
[True, False, 'a', 'ciao!', {}, 1, 2, {1: 'un', 2: 'deux'}]

Well, interesting, isn't it? You can, as a choice, append to a list either a
dictionary's keys or its key:value pairs, both without even explicitely
calling the append() method.
Now, what about adresses? (let's not be distracted)

>>> id(x)
10763120
>>> for i in range(len(x)):
        print id(x[i])        # items' addresses
504028992
504028976
6759872
10763168
10790352
7678960
7677952
10639664
>>> id(1);id(2)
7678960
7677952    # right!
>>> id(d)
10639664     # right again!

True and False are rather close, but not aside; 1 and 2 are strangely far
(?). All items are separated from the list, they're not "in" the list. Which
means, as expected, that the list holds its elements' addresses -- only its
elements' addresses.

>>> id(d)
10639664
>>> for i in range(len(d)):
        print id(d.keys()[i])    # keys' addresses
7678960
7677952
>>> for i in range(len(d)):
        print id(d[d.keys()[i]])    # values' ones
10763872
10762944

It also clear that for a dictionary the dict. itself, its keys and its
values are totally separated.
Just as a recall about value changes by immutable types:

>>> t='Freude, schöner Götterfunken,...' ; id(t)
6742216
>>> t='Ô joie, divine étincelle de beauté...' ; id(t)
10781656

The (whole) variable's address changes. What happens when I change x, now?
There are two kinds of changes:

>>> id(x); id(x[3])
10763120
10763168

>>> x[3]='olà!'    # first --I mean zeroth: partial change
>>> id(x); id(x[3])
10763120
10764064

>>> x=[1,'a',d]    # first: global change

>>> id(x)
10763216

By immutable types, only the item(s) changed move(s) when they're directly
addressed: the list's address hasn't changed. But the global variable moves
when addressed as a whole.
[Just as foretaste (?) on this topic...]

denis








More information about the Tutor mailing list