I have a hierarchy of objects in a python module as follows:
class BaseObject(object):
initialized = False
def __init__(self):
self._initialize()
@classmethod
def _initialize(cls):
print "cls.initialized = "+str(cls.initialized)
if not cls.initialized:
cls.x = 1
cls.initialized = True
class ObjectOne(BaseObject):
@classmethod
def double_x(cls):
cls.x = cls.x * 2
print cls.x
class ObjectTwo(BaseObject):
@classmethod
def triple_x(cls):
cls.x = cls.x * 3
print cls.x
if __name__ == '__main__':
obj_1 = ObjectOne()
obj_1.double_x()
obj_2 = ObjectTwo()
obj_2.triple_x()
When I run this module I would like the output to be:
cls.initialized = False
2
cls.initialized = True
6
But what I get is:
cls.initialized = False
2
cls.initialized = False
3
What do I not understand?
You need to use the full classname to set class variables. cls
in double_x
and tripple_x
will refer to subclasses (ObjectOne
and ObjectTwo
, respectively), and setting attributes on those subclasses will store new variables, not alter the class variable BaseObject.x
. You can only alter base class variables by directly accessing them.
Using your code, we get:
>>> obj_1 = ObjectOne()
cls.initialized = False
>>> obj_1.double_x()
2
>>> obj_2 = ObjectTwo()
cls.initialized = False
>>> obj_2.triple_x()
3
>>> BaseObject.x
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: type object 'BaseObject' has no attribute 'x'
>>> BaseObject.initialized, ObjectOne.initialized, ObjectOne.x, ObjectTwo.initialized, ObjectTwo.x
(False, True, 2, True, 3)
What happened is that in _initialize()
, cls
was set to ObjectOne
or ObjectTwo
, depending on what instance you created, and each subclass got their own copies of the variables initialized
and x
.
Using BaseObject._initialize()
(to ensure that BaseObject
is initialized, and not the subclasses) gives:
>>> obj_1 = ObjectOne()
cls.initialized = False
>>> obj_1.double_x()
2
>>> obj_2 = ObjectTwo()
cls.initialized = True
>>> obj_2.triple_x()
3
>>> BaseObject.x, ObjectOne.x, ObjectTwo.x
(1, 2, 3)
>>> BaseObject.initialized
True
>>> 'x' in ObjectOne.__dict__
True
>>> 'initialized' in ObjectOne.__dict__
False
>>> 'initialized' in ObjectTwo.__dict__
False
So now _initialize()
used BaseObject
as the target to set initialized
and the initial value for x
, but double_x
and triple_x
still used their own subclasses to set the new value of x
and are not sharing that value through BaseObject
.
The only option you have to set class variables on a specific base class is to refer to it directly in all class methods:
class BaseObject(object):
initialized = False
def __init__(self):
BaseObject._initialize()
@classmethod
def _initialize(cls):
print "cls.initialized = "+str(cls.initialized)
if not cls.initialized:
cls.x = 1
cls.initialized = True
class ObjectOne(BaseObject):
@classmethod
def double_x(cls):
BaseObject.x = BaseObject.x * 2
print cls.x
class ObjectTwo(BaseObject):
@classmethod
def triple_x(cls):
BaseObject.x = BaseObject.x * 3
print cls.x
which would give:
>>> obj_1 = ObjectOne()
cls.initialized = False
>>> obj_1.double_x()
2
>>> obj_2 = ObjectTwo()
cls.initialized = True
>>> obj_2.triple_x()
6
Note that I called BaseObject._initialize()
to make sure that cls
is BasObject
and not a subclass. Then, when setting x
the double_x
and triple_x
methods still refer directly to BaseObject
to ensure that the variable is set directly on the base class. When reading the value of x
the above example still uses cls
, which uses the class MRO to find x
on the base class when not set locally.
You have two issues.first of all in order to call the class method inside the class,you must use the COMPLETE name of the class:BaseObject._initialize()
second of all, every time you make a new instance of ObjectOne
or ObjectTwo
,you are overwriting the BaseObject.x
within its environment,so others use the initialized x
attribute instead of the changed one.to fix this you must change two lines:
cls.x = cls.x *
2 To BaseObject.x = cls.x * 2
and
cls.x = cls.x * 3
To BaseObject.x = cls.x * 3
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