pythonclassstatic-variables

Does Class Variable is common among objects in Python


Suppose I want to implement common variable among objects using class variables (similar to static in java/c++).

When I access class variable using object, it shows default value. Then I updaded class variable via object, its not updating class variable for other objects. It is showing old value in when class variable accessed via other objects or Class Name directly.

Why is it so, and what is the way in python to make class/static variable (common among objects)

# Class for Computer Science Student
class CSStudent:
    
    stream = 'cse'               # Class Variable
    def __init__(self,name):
        self.name = name     # Instance Variable

# Objects of CSStudent class
a = CSStudent('Geek')
b = CSStudent('Nerd')


print(a.stream) # prints "cse"
print(b.stream) # prints "cse"
print(a.name) # prints "Geek"
print(b.name) # prints "Nerd"

# Class variables can be accessed using class
# name also
print(CSStudent.stream) # prints "cse"

# Now if we change the stream for just a it won't be changed for b
a.stream = 'ece'
b.stream = 'abc'
print(CSStudent.stream)  # prints 'ece'
print(a.stream) # prints 'ece'
print(b.stream) # prints 'abc'

Solution

  • You need to know the lookup procedure.

    First off, both instances and classes have their own namespaces. Only the class variables are shared between all instances. The namespace of classes and instances are accessible via __dict__ attribute.

    When you access an attribute from an instance, Python first looks at the namespace of the instance, if it can find it, it returns it, otherwise it's gonna find it in its class!

    class CSStudent:
        stream = "cse"
    
    a = CSStudent()
    b = CSStudent()
    print("stream" in a.__dict__)          # False
    print("stream" in b.__dict__)          # False
    print("stream" in CSStudent.__dict__)  # True
    
    print(a.stream)  # cse
    print(b.stream)  # cse
    

    So a and b doesn't have that stream, only the class has.

    Now a.stream = "something" will add that attribute to the namespace of that specific instance.

    a.stream = "something"
    print("stream" in a.__dict__)          # True
    print("stream" in b.__dict__)          # false
    print("stream" in CSStudent.__dict__)  # True
    

    Now if you access a.stream it finds it in the namespace of a and returns it, but because b doesn't have that, it will find it in the class.(the shared one)

    print(a.stream)  # something
    print(b.stream)  # cse
    

    If you want to the change to reflect all instances, you need to change it on the class which has it shared between all instances.

    class CSStudent:
        stream = "cse"
    
    a = CSStudent()
    b = CSStudent()
    print(a.stream)     # cse
    print(b.stream)     # cse
    CSStudent.stream = "something"
    print(a.stream)     # something
    print(b.stream)     # something
    

    Always think about who has what. Then consider the precedence, instance namespaces are checked first.

    Note: I simplified the explanation by not mentioning what descriptors are because we don't have one here. Take a look at it later. You would be surprised to see that in fact always class namespaces are checked first!