Python 3 – Does the direct manipulation of a class' attribute override the same attribute for its objects making the attribue purely static?

Question:

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 🙂

Asked By: Solomon

||

Answers:

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 to C.count for any c such that
isinstance(c, C) holds, unless overridden by c itself or by some
class on the base-class search path from c.__class__ back to C.

Caution: within a method of C, an assignment like self.count = 42
creates a new and unrelated instance named “count” in self’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.

Answered By: trincot