Implied instance attribute creation when referencing a class attribute

Russell Warren russandheather at gmail.com
Mon Jan 16 22:53:36 EST 2006


> I can see how this can be confusing, but I think the confusion here is
> yours, not Pythons ;)

This is very possible, but I don't think in the way you describe!

> self.I += 10 is an *assignment*. Like any assignment, it causes the
> attribute in question to be created

... no it isn't.  The += is an operator.  Look at the example I
included with non_existent_var above.  If that doesn't do it for you,
pop open a clean python shell and do this one:

>>> x += 2
Traceback (most recent call last):
  File "<string>", line 1, in <string>
NameError: name 'x' is not defined

Note that x doesn't exists and it does not create it.  You can't
normally operate on something before it is created - Python won't
create it for you (which is why I was surprised by the class attribute
behavior in the first post).

> If you write out the longhand for += it becomes totally obvious what
> is happening and why it makes sense:

Not true as above.  The longhand for 'self.I += 1' is 'self.I = self.I
+ 1', which normally needs self.I to exist due to the RHS of this.

> So your case 1 is actually exactly what is happening! Python is
> getting a hold of foo.I and incrementing it

Nope.  My case 1 would have the 'self.I += 1' modifying the actual
class attribute, not some new instance attribute and this is definitely
NOT happening.  Maybe my example was bad?  Try this one instead:

>>> class foo(object):
...   I = 1
...   def __init__(self):
...     self.I += 123455
...
>>> a=foo()
>>> a.I
123456
>>> foo.I
1
>>> del a.I
>>> a.I
1

Note that we ended up binding a new "I" to the 'a' instance with the
'self.I += 1' statement, and it started with the value of 1 (the value
of the base class attribute).  I tried to make it clear in the example
by wiping out the local copy, which then reveals the base class
attribute when you go for it again.

The fact that there is a local I being made with the value of the base
class attribute says that Python is essentially adding the line 'self.I
= foo.I' as in the code below.

>>> class foo(object):
...   I = 123455
...   def __init__(self):
...     self.I = foo.I  # unnecessary since python seems to do it in
the next line
...     self.I += 1
...
>>> a=foo()
>>> b=foo()
>>> c=foo()
>>> print c.I, foo.I
123456 1

For kicks I added the b and c creations to show that at no time did the
+= operator get a hold of the foo base class as you state.  It stayed
untouched at 1 the whole time.  To do that you need to reference foo
itself as in the following case:

>>> class foo(object):
...   I = 0
...   def __init__(self):
...     foo.I += 1
...     self.I = foo.I
...
>>> a=foo()
>>> b=foo()
>>> c=foo()
>>> print a.I, b.I, c.I, foo.I
1 2 3 3
>>> del a.I
>>> a.I
3

Here it of course *did* increment the base foo attribute since it was
directly referenced.  'a.I' stays as 1 here because I rebound a new
instance attribute I on top with a copy of the base foo.I value due to
it being immutable (a bit weird to use the same name, but I'm trying to
show something) and it is what is retrieved first by Python (local
dictionary first, if not found it goes to the base class).  When I
clear I from the local __dict__ with the del, you see that future
self.I references skip out to the base class attribute since there is
no instance I attribute anymore.

A bit of a sidetrack there... still curious why python decides to
auto-create the variable for you in this particular case.  Any other
takers?

Russ




More information about the Python-list mailing list