class variable won't icrement!

Eric Brunel eric.brunel at pragmadev.com
Thu Sep 12 05:48:33 EDT 2002


Gumuz wrote:

> Hello everyone,
> 
> I have something very strange and i can not understand it. probably, it's
> not _that_ strange : )
> 
> I have this simple class which registers itself on a class variable in the
> following code:
> 
> 
----------------------------------------------------------------------------
> ------
> class test:
>     dict = {}
>     id = 1
>     def __init__(self, name):
>         self.dict[name] = self
>         self.id = self.id + 1
> 
>     def show(self):
>         print "class variable dict element count: ", str(len(self.dict))
>         print "class variable id: ", self.id
> 
----------------------------------------------------------------------------
> --------
> 
> now, look at the following:
> 
>>>> john = test("john")
>>>> frank = test("frank")
>>>> bill = test("bill")
>>>> bill.show()
> class variable dict element count:  3
> class variable id:  2
>>>> frank.show()
> class variable dict element count:  3
> class variable id:  2
> 
> The dict variable acts fine, maintaining it's content throughout every
> instance of the class.
> The id variable however, seems to be only incremented within the instance.
> In the end this variable should've been 4.
> 
> Could someone explain this to me? Is there something about class variables
> whta i should know?

Yep: to access them, you'd better always use the syntax <class 
name>.<attribute name>. Instances do in fact "inherit" the attributes of 
their class, but it leads to misunderstandings just as yours. In your 
example, here is what happens:

> class test:
>     dict = {}

Here, you create an attribute dict for class test. The value for this 
attribute is an *object*, which is *mutable*, that happens to be an empty 
dictionary.

>     id = 1

Here, you create an attribute id for class test. The value for this 
attribute is an *integer*, which is *not* an object, and is *immutable*.

>     def __init__(self, name):
>         self.dict[name] = self

Here, you use the attribute dict, inherited from the class, and you modify 
it. These modifications happens *in place*, which means that the pointed 
dictionary is actully modified. Since both test.dict and self.dict point to 
the same object, they'll both see the modification. This is exactly the 
same if you do:

a = {}
b = a
b['foo'] = 'bar'
print a

You'll see that a has been modified. In your example, here is roughly what 
happens:

test.dict = {}
self.dict = test.dict    # Implicit inheritance from class to instance
self.dict[name] = self

>         self.id = self.id + 1

This is a completely different story: here, you *recreate* an instance 
attribute named id, based on the id attribute inherited from the class. So, 
in fact, you're doing:

test.id = 1
self.id = test.id    # Again, implicit inheritance from class to instance
self.id = self.id + 1

just as in:

a = 1
b = a
b = b + 1
print a

Of course, a is not modified here...

I always found that inheritance of class attributes to instance attributes 
was really misleading, and I even think it would be better if it were 
simply removed from Python. IMHO, it would far more conform to the 
"explicit is better than implicit" rule that is one of the basis of Python.

To avoid its drawbacks:
- never declare as a class attribute something that's actually an instance 
attribute
- always use <class name>.<attribute name> to access class attributes, 
especially when you modify them

HTH
-- 
- Eric Brunel <eric.brunel at pragmadev.com> -
PragmaDev : Real Time Software Development Tools - http://www.pragmadev.com



More information about the Python-list mailing list