Inexplicable behavior in simple example of a set in a class
steve+comp.lang.python at pearwood.info
Sun Jul 3 04:25:29 CEST 2011
Saqib Ali wrote:
> I have written two EXTREMELY simple python classes. One class
> (myClass1) contains a data attribute (myNum) that contains an integer.
> The other class (myClass2) contains a data attribute (mySet) that
> contains a set.
> I instantiate 2 instances of myClass1 (a & b). I then change the value
> of a.myNum. It works as expected.
> Then I instantiate 2 instances of myClass2 (c & d). I then change the
> value of c.mySet. Bizarrely changing the value of c.mySet also affects
> the value of d.mySet which I haven't touched at all!?!?!
But that is wrong -- you HAVE touched it. Look carefully: in myClass2, you
mySet = sets.Set(range(1,10))
mySet is a class attribute, shared by ALL instances, and mySet.clear()
modifies it in place. This is exactly the same as this snippet:
>>> a = set([1, 2, 3])
>>> b = a
In Python, attributes assigned in the class scope are shared between all
instances. Attributes assigned directly on self are not:
a = set([1, 2, 3])
self.b = set([1, 2, 3])
>>> x = Test()
>>> y = Test()
>>> x.a is y.a # The same set is shared by both instances.
>>> x.b is y.b # Each instance gets its own set.
So why does myClass1 behave differently? Simple: look at the clear method:
myNum = 9
self.myNum = 0
It assigns a new attribute, rather than modifying the object in place!
Assignment to self.myNum creates a new unshared attribute:
>>> z = myClass1()
>>> z.__dict__ # No instance attributes yet.
>>> z.__dict__ # Now there is one.
>>> z.__class__.myNum # the class attribute still exists
The simplest way to fix this is to move the declaration of self.mySet into
the __init__ method.
More information about the Python-list