为什么Python似乎将实例变量视为对象之间的共享?

时间:2012-01-14 05:52:16

标签: python oop object

今天我正在编写一个简单的脚本,当时我注意到Python处理实例变量的方式有一个奇怪的怪癖。

假设我们有一个简单的对象:

class Spam(object):
    eggs = {}

    def __init__(self, bacon_type):
        self.eggs["bacon"] = bacon_type

    def __str__(self):
       return "My favorite type of bacon is " + self.eggs["bacon"]

我们用不同的参数创建这个对象的两个实例:

spam1 = Spam("Canadian bacon")
spam2 = Spam("American bacon")

print spam1
print spam2

结果令人费解:

My favorite type of bacon is American bacon
My favorite type of bacon is American bacon

似乎“egg”字典在所有不同的“垃圾邮件”实例之间共享 - 或者每次创建新实例时都会被覆盖。这在日常生活中并不是真正的问题,因为我们可以通过在初始化函数中声明实例变量来解决它:

class Spam(object):

    def __init__(self, bacon_type):
        self.eggs = {}
        self.eggs["bacon"] = bacon_type

    def __str__(self):
        return "My favorite type of bacon is " + self.eggs["bacon"]

spam1 = Spam("Canadian bacon")
spam2 = Spam("American bacon")

print spam1
print spam2

以这种方式编写代码,结果就是我们所期望的:

My favorite type of bacon is Canadian bacon
My favorite type of bacon is American bacon

因此,虽然我并不担心这种行为,但我不明白为什么Python以这种方式工作。任何人都可以对此有所了解吗?

3 个答案:

答案 0 :(得分:7)

正如Ignacio发布的那样,在Python中分配给类范围的变量是类变量。基本上,在Python中,类只是class语句下的语句列表。一旦语句列表完成执行,Python就会挖掘在执行过程中创建的任何变量,并从中创建一个类。如果你想要一个实例变量,你实际上必须将它分配给实例。

另一方面:听起来你可能是从Java(或类似Java)的角度来看这个。所以也许你知道因为Java需要显式声明变量,所以它需要在类范围内有实例变量声明。

class Foo {
    String bar;
    public Foo() {
        this.bar = "xyz";
    }
}

请注意,只有声明属于类范围。换句话说,为该变量分配的 memory 是类“template”的一部分,但变量的实际不是。

Python不需要变量声明。所以在Python翻译中,你只需删除声明。

class Foo:
    # String bar;  <-- useless declaration is useless
    def __init__(self):
        self.bar = "xyz"

内存将在需要时分配;只有作业才被写出来。这就像构造函数一样,就像在Java中一样。

答案 1 :(得分:5)

这不是一个实例变量,而是一个类变量。通过实例访问它的事实是无关紧要的;它仍然是同一个对象。

答案 2 :(得分:2)

与编译语言不同,Python中的class语句实际上是执行代码,并在内存中创建类对象(不是实例!)。

运行class块时定义的任何符号都属于类本身。这包括变量,例如第一个示例中的eggs变量,以及您定义的__init____str__方法。所有这些都是在定义类时创建的,它们都是类的一部分。

在您实际创建对象的实例并运行__init__方法并且 属于self的属性之前,不会创建实例变量。< / p>

因此,当python解释器执行时

class Spam(object):
    eggs = {}

    def __init__(self):
        <stuff>
    def __str__(self):
        <other stuff>

它实际上是在运行时构建一个类对象。它执行代码“eggs={}”,并执行两个def语句,并构建一个具有三个属性的类:eggs__init__和{{1} }。

稍后,执行时

__str__

然后它创建一个新实例,并运行其spam1 = Spam() 方法。当然,__init__方法本身属于该类;它在所有实例之间共享,就像__init__属性一样。

实例本身作为eggs参数传入,您在其上定义的任何内容都属于该实例。这就是为什么self必须传递到每个类方法的原因 - 在python中,方法实际上属于类本身,self是你必须引用实例的唯一方法。