5

我在 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

我不明白什么?

4

2 回答 2

7

您需要使用完整的类名来设置类变量。clsindouble_xtripple_x将引用子类(ObjectOneObjectTwo,分别),并且在这些子类上设置属性将存储变量,而不是更改类变量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)

发生的事情是 in _initialize(),cls被设置为ObjectOneor ObjectTwo,这取决于您创建的实例,并且每个子类都有自己的变量副本initializedx.

使用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_xtriple_xxBaseObject

您必须在特定基类上设置类变量的唯一选择是在所有类方法中直接引用它:

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的时候还是直接引用来保证变量是直接设置在基类上的。上面例子的值在读取的时候仍然使用 ,在本地没有设置的时候使用类 MRO 在基类上查找。double_xtriple_xBaseObjectxclsx

于 2012-12-30T17:44:28.423 回答
1

您有两个问题。首先,为了在类中调用类方法,您必须使用该类的完整名称:BaseObject._initialize() 其次,每次创建 or 的新实例时ObjectOneObjectTwo您都会覆盖BaseObject.x其环境中的,所以其他人使用初始化x属性而不是更改的属性。要解决此问题,您必须更改两行:

cls.x = cls.x *2 至BaseObject.x = cls.x * 2

cls.x = cls.x * 3BaseObject.x = cls.x * 3

于 2012-12-30T18:20:29.200 回答