我在python模块中有一个对象层次结构,如下所示:
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()
当我运行此模块时,我希望输出为:
cls.initialized = False
2
cls.initialized = True
6
但我得到的是:
cls.initialized = False
2
cls.initialized = False
3
我不明白什么?
答案 0 :(得分:7)
您需要使用完整的类名来设置类变量。 cls
和double_x
中的tripple_x
将分别引用子类(ObjectOne
和ObjectTwo
),并在这些子类上设置属性将存储 new < / em>变量,不改变类变量BaseObject.x
。您可以通过直接访问它们来仅更改基类变量。
使用您的代码,我们得到:
>>> 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)
在_initialize()
中,cls
设置为ObjectOne
或ObjectTwo
,取决于您创建的实例,每个子类的拥有变量initialized
和x
的副本。
使用BaseObject._initialize()
(以确保初始化BaseObject
而不是子类)给出:
>>> 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
所以现在_initialize()
使用BaseObject
作为设置initialized
的目标和x
的初始值,但仍然double_x
和triple_x
使用自己的子类设置x
的 new 值,并且不通过BaseObject
分享该值。
在特定基类上设置类变量的唯一选择是直接在所有类方法中引用它:
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
会给出:
>>> obj_1 = ObjectOne()
cls.initialized = False
>>> obj_1.double_x()
2
>>> obj_2 = ObjectTwo()
cls.initialized = True
>>> obj_2.triple_x()
6
请注意,我致电BaseObject._initialize()
以确保cls
是BasObject
而不是子类。然后,当设置 x
时,double_x
和triple_x
方法仍然直接引用BaseObject
以确保直接在基类上设置变量。当读取 x
的值时,上面的示例仍然使用cls
,它在未在本地设置时使用类MRO在基类上查找x
。 / p>
答案 1 :(得分:1)
您有两个问题。首先,为了在类中调用类方法,您必须使用类的 COMPLETE 名称:BaseObject._initialize()
第二,每次你创建ObjectOne
或ObjectTwo
的新实例时,你都会覆盖其环境中的BaseObject.x
,因此其他人使用初始化的x
属性而不是改变了一个。解决这个问题你必须改变两行:
cls.x = cls.x *
2到BaseObject.x = cls.x * 2
和
cls.x = cls.x * 3
致BaseObject.x = cls.x * 3