While learning about how classes work in Python I came across a class definition example which behaved kind of strangely in my eyes.
The purpose of the example was to demonstrate how the behaviour of a static variable can be achieved in Python. The example was written as follows:
class MemberCounter:
members = 0
def init(self):
MemberCounter.members += 1
m1 = MemberCounter()
m1.init()
m2 = MemberCounter()
m2.init()
after setting up the class and creating the objects, I printed the values of the 'members' attribute. These were the results:
MemberCounter.members = 2
m1.members = 2
m2.members = 2
And that's when I got confused. While I was expecting for 'MemberCounter.members = 2' the two other results made no sense to me - why would both of 'm1' and 'm2' objects' 'members' value be equal to 2? I thought that both of the values should have been 0 - if the only attribute that was chaged is the 'members' attribute which was attached to the MemberCounter class why would it cause any change to the own unique 'members' value of each of the class' objects. It looks like the fact that the 'members' attribute is addresed like 'MemberCounter.members += 1' in the init() function of each object, completely overrides the unique values which m1.members and m2.members refer to and redirects their pointers to the MemberCounter.members value making all the three pointers point at the same value
==> m1.members = m2.members = MemberCounter.members.
Moreover, I have tried defining the class in an opossite way (Increasing self.members instead of MemberCounter.members):
class MemberCounter:
members = 0
def init(self):
self.members += 1
m1 = MemberCounter()
m1.init()
m2 = MemberCounter()
m2.init()
This definition yielded logical results (which got me curious about the above mentioned strange behaviour even more):
MemberCounter.members = 0
m1.members = 1
m2.members = 1
In short, I was curious about why the first class definition behaves in such a strange way? Why the mere 'MemberCounter.members += 1' statement completely erased 'm1.members' and 'm2.members' own unique value and made it equal to the MemberCounter.members value.
I hope I was able to clearly present my problem and I will be extremly happy to get an insight about this strange behaviour :)
That you can read a static attribute with instance.attribute
notation as alternative to the more natural class.attribute
notation, is an intended feature in Python.
From the documentation:
Both static data and static methods (in the sense of C++ or Java) are supported in Python.
For static data, simply define a class attribute. To assign a new value to the attribute, you have to explicitly use the class name in the assignment:
class C: count = 0 # number of times C.__init__ called def __init__(self): C.count = C.count + 1 def getcount(self): return C.count # or return self.count
c.count
also refers toC.count
for anyc
such thatisinstance(c, C)
holds, unless overridden byc
itself or by some class on the base-class search path fromc.__class__
back toC
.Caution: within a method of
C
, an assignment likeself.count = 42
creates a new and unrelated instance named “count” inself
’s own dict. Rebinding of a class-static data name must always specify the class whether inside a method or not:
C.count = 314
The paragraph just below the first code block explains your doubts. The "Caution" paragraph explains what you found logical.