3

I wrote a simple program.

class Sample:

    num =45

    def __init__(self):
        print('Inside constructor')

    @classmethod
    def modifyUsingClassMethod(cls):
        cls.num = cls.num + 45

    @staticmethod
    def modifyUsingStaticMethod():
        Sample.num = Sample.num+5

s1 = Sample()
s2 = Sample()

s1.modifyUsingClassMethod()
print(s1.num, s2.num)

s1.num = s1.num + 5
print(s1.num)

s1.modifyUsingClassMethod()
print(s1.num, s2.num)

s1.modifyUsingStaticMethod()
print(s1.num, s2.num)

Output:

Inside constructor
Inside constructor
90 90
95
95 135
95 140

Can anyone explain how and why the @staticmethod and @classmethod are acting on the variable 'num'?. Why does the output shows 95,135 even after I changed the value of num using s1 instance using modifyUsingClassMethod() and why not it is updating in both cases using @staticmethod and @classmethod?

I guess when I am referring to the variable num using class object then python is treating the variable num as an instance variable but when I change the variable num using Class name then the value is not updating in the s1 but in s2. I am highly confused how @classmethod and @staticmethod works.

1
  • I think you are guesing it right. Commented Mar 8, 2018 at 6:56

2 Answers 2

3

Both your class-method and your static-method only ever change the class-level variable. The issue is that you've shadowed your class-variable num inside your instance variable, s1, when you did this:

s1.num = s1.num + 5

This creates an instance variable that shadows the class variable in the instances namespace. When you access an object, the instance's namespace will be checked, if an attribute with that name isn't found, it will try the classes name-space, and then it will check the namespaces of all the classes in the method-resultion-order: the MRO (this is inheritance).

So consider your example:

In [1]: class Sample:
   ...:     num =45
   ...:
   ...:     def __init__(self):
   ...:         print('Inside constructor')
   ...:
   ...:     @classmethod
   ...:     def modifyUsingClassMethod(cls):
   ...:         cls.num = cls.num + 45
   ...:
   ...:     @staticmethod
   ...:     def modifyUsingStaticMethod():
   ...:         Sample.num = Sample.num+5
   ...:

In [2]: s1 = Sample()
   ...: s2 = Sample()
   ...:
   ...: s1.modifyUsingClassMethod()
   ...: print(s1.num,s2.num)
   ...:
   ...: s1.num = s1.num + 5
   ...: print(s1.num)
   ...:
   ...: s1.modifyUsingClassMethod()
   ...: print(s1.num,s2.num)
   ...:
   ...: s1.modifyUsingStaticMethod()
   ...: print(s1.num,s2.num)
   ...:
Inside constructor
Inside constructor
90 90
95
95 135
95 140

And now look at the objects:

In [4]: vars(Sample)
Out[4]:
mappingproxy({'__dict__': <attribute '__dict__' of 'Sample' objects>,
              '__doc__': None,
              '__init__': <function __main__.Sample.__init__>,
              '__module__': '__main__',
              '__weakref__': <attribute '__weakref__' of 'Sample' objects>,
              'modifyUsingClassMethod': <classmethod at 0x107c3fe48>,
              'modifyUsingStaticMethod': <staticmethod at 0x107c3ff98>,
              'num': 140})

In [5]: vars(s1)
Out[5]: {'num': 95}

In [6]: vars(s2)
Out[6]: {}

You can clearly see the namespace of s1 has num in it, shadowing the one in the namespace of Sample.

Note what happens when we delete num from the instances name-space:

In [11]: del s1.num

In [12]: s1.num
Out[12]: 140
Sign up to request clarification or add additional context in comments.

Comments

1

The operation:

s1.num = s1.num + 5

causes s1 to contain a local copy of .num. If you remove that from your test code, you will see that without that, the properties continue to track themselves over time.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.