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

Why does Python seem to treat instance variables as shared between objects?

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

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

1
2
3
4
5
6
7
8
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"]

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

1
2
3
4
5
spam1 = Spam("Canadian bacon")
spam2 = Spam("American bacon")

print spam1
print spam2

结果令人费解:

1
2
My favorite type of bacon is American bacon
My favorite type of bacon is American bacon

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
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

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

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

所以,虽然我没有被这种行为所阻碍,但我不理解为什么Python是这样工作的。有人能解释一下吗?


正如ignacio所发布的,在python的类范围内分配给的变量是类变量。基本上,在Python中,类只是class语句下的语句列表。一旦语句列表完成执行,python就会收集在执行过程中创建的所有变量,并从中生成一个类。如果您想要一个实例变量,实际上您必须将它赋给该实例。

另一个注意事项:听起来你可能会从Java(或类似Java)的角度来看这个问题。也许你知道,因为Java需要显式声明变量,所以它需要在类作用域中有实例变量声明。

1
2
3
4
5
6
class Foo {
    String bar;
    public Foo() {
        this.bar ="xyz";
    }
}

注意,只有声明在类范围内。换句话说,为该变量分配的内存是类"template"的一部分,但变量的实际值不是。

python不需要任何变量声明。所以在python翻译中,只需删除声明。

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

内存将在需要时被分配;只有分配是实际写出来的。这在构造器中,就像在Java中一样。


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


与编译语言不同,python中的class语句实际上是执行并创建类对象(而不是实例)的代码。在记忆中。

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

在实际创建对象的实例并且运行__init__方法之前,不会创建实例变量,这些变量必须是self的属性。

所以,当python解释器执行时

1
2
3
4
5
6
7
class Spam(object):
    eggs = {}

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

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

稍后,当它执行时

1
spam1 = Spam()

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

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