How would you I came up with this interesting (at least to me) example.
import numpy as np
class Something(object):
a = np.random.randint(low=0, high=10)
def do(self):
self.a += 1
print(self.a)
if __name__ == '__main__':
something = Something()
print(something.__str__())
something.do()
something2 = Something()
print(something2.__str__())
something2.do()
something3 = Something()
print(something3.__str__())
something3.do()
The above prints the following in the console:
$ python test.py
<__main__.Something object at 0x7f03a80e0518>
1
<__main__.Something object at 0x7f03a80cfcc0>
1
<__main__.Something object at 0x7f03a80cfcf8>
1
I'm a bit confused because I (wrongly) assumed the value of a
would have increased.
I'm able to obtain the behaviour I would expect if I use the @classmethod
decorator.
import numpy as np
class Something(object):
a = np.random.randint(low=0, high=10)
@classmethod
def do(cls):
cls.a += 1
print(cls.a)
if __name__ == '__main__':
something = Something()
print(something.__str__())
something.do()
something2 = Something()
print(something2.__str__())
something2.do()
something3 = Something()
print(something3.__str__())
something3.do()
This correctly prints the following in the console.
python test.py
<__main__.Something object at 0x7faac77becc0>
3
<__main__.Something object at 0x7faac77becf8>
4
<__main__.Something object at 0x7faac77c3978>
5
Now, I'm wondering in the first example, when I'm calling self.a
, what I'm accessing? It's not a class variable since I don't seem to be able to change its value. It's not an instance variable either, since this seems to be shared among different objects of the same class. How would you call it?
Is this a class variable that I'm using in the wrong way? I know the cls
name if a convention, so maybe I'm truly accessing a class variable, but I'm not able to change its value because I haven't decorate the method with the @classmethod
decorator.
Is this a sort of illegitimate use of the language? I mean something it's best practice to not do in order to avoid introduce a bug on a later stage?
What is happening is that self.a
refers to two things at different times.
When no instance variable exists for a name, Python will lookup the value on the class. So the value retrieved for self.a
will be the class variable.
But when setting an attribute via self
, Python will always set an instance variable. So now self.a
is a new instance variable whose value is equal to the class variable + 1. This attribute shadows the class attribute, which you can no longer access via self
but only via the class.
(One minor point, which has nothing to do with the question: you should never access double-underscore methods directly. Instead of calling something2.__str__()
, call str(something2)
etc.)
Answer by Daniel Roseman clearly explains the problem. Here are some additional points and hope it helps. You can use type(self).a instead of self.a. Also look at the discussions Python: self vs type(self) and the proper use of class variables and Python: self.__class__ vs. type(self)
import numpy as np
class Something(object):
a = np.random.randint(low=0, high=10)
def do(self):
type(self).a += 1
print(type(self).a)
if __name__ == '__main__':
something = Something()
print(str(something ))
something.do()
something2 = Something()
print(str(something2))
something2.do()
something3 = Something()
print(str(something3))
something3.do()
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With