Reputation: 420
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.
Upvotes: 3
Views: 2150
Reputation: 95873
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
Upvotes: 3
Reputation: 49774
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.
Upvotes: 1