22

以下看起来很奇怪.. 基本上, somedata 属性似乎在继承自的所有类之间共享the_base_class

class the_base_class:
    somedata = {}
    somedata['was_false_in_base'] = False


class subclassthing(the_base_class):
    def __init__(self):
            print self.somedata


first = subclassthing()
{'was_false_in_base': False}
first.somedata['was_false_in_base'] = True
second = subclassthing()
{'was_false_in_base': True}
>>> del first
>>> del second
>>> third = subclassthing()
{'was_false_in_base': True}

self.somedata在函数中定义__init__显然是解决这个问题的正确方法(所以每个类都有自己的somedatadict)——但是什么时候需要这种行为呢?

4

3 回答 3

24

你是对的,somedata在类的所有实例及其子类之间共享,因为它是在类定义时创建的。线条

somedata = {}
somedata['was_false_in_base'] = False

在定义类时执行,即在解释器遇到class语句时执行 -而不是在创建实例时执行(想想 Java 中的静态初始化程序块)。如果类实例中不存在属性,则检查类对象的属性。

在类定义时,您可以运行任意代码,如下所示:

 import sys
 class Test(object):
     if sys.platform == "linux2":
         def hello(self):
              print "Hello Linux"
     else:
         def hello(self):
              print "Hello ~Linux"

在 Linux 系统上,Test().hello()将打印Hello Linux,在所有其他系统上,其他字符串将被打印。

相反,在实例化__init__时创建的对象仅属于实例(当它们被分配给时):self

class Test(object):
    def __init__(self):
        self.inst_var = [1, 2, 3]

在许多情况下,在类对象而不是实例上定义的对象可能很有用。例如,您可能希望缓存类的实例,以便可以共享具有相同成员值的实例(假设它们应该是不可变的):

class SomeClass(object):
    __instances__ = {}

    def __new__(cls, v1, v2, v3):
        try:
            return cls.__insts__[(v1, v2, v3)]
        except KeyError:
            return cls.__insts__.setdefault(
               (v1, v2, v3), 
               object.__new__(cls, v1, v2, v3))

大多数情况下,我将类体中的数据与元类或通用工厂方法结合使用。

于 2008-10-15T22:27:18.713 回答
12

请注意,您看到的部分行为是由于somedata是 a dict,而不是像 a 这样的简单数据类型bool

例如,看看这个不同的例子,它的行为不同(尽管非常相似):

class the_base_class:
    somedata = False

class subclassthing(the_base_class):
    def __init__(self):
        print self.somedata


>>> first = subclassthing()
False
>>> first.somedata = True
>>> print first.somedata
True
>>> second = subclassthing()
False
>>> print first.somedata
True
>>> del first
>>> del second
>>> third = subclassthing()
False

这个例子的行为与问题中给出的不同的原因是因为这里first.somedata被赋予了一个新值(对象True),而在第一个例子中first.somedata(以及其他子类实例)引用的 dict 对象正在被修改。

有关进一步说明,请参阅 Torsten Marek 对此答案的评论。

于 2008-10-15T22:40:10.013 回答
3

我认为理解这一点(以便您可以预测行为)的最简单方法是意识到您somedata是该类的属性,而不是该类的实例,如果您以这种方式定义它。

实际上始终只有一个somedata,因为在您的示例中,您没有分配给该名称,而是使用它来查找字典,然后为其分配一个项目(键,值)。这是一个陷阱,它是 python 解释器工作方式的结果,一开始可能会令人困惑。

于 2008-10-15T22:55:23.133 回答