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

6 投票
3 回答
2336 浏览
提问于 2025-04-17 10:14

今天我在写一个简单的脚本时,发现了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

看起来“eggs”这个字典在所有不同的“Spam”实例之间是共享的——要么就是每次创建新实例时都会被覆盖。这在日常生活中其实不是个大问题,因为我们可以通过在初始化函数中声明实例变量来解决这个问题:

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 个回答

2

和编译语言不同,在Python中,class语句实际上是可以执行的代码,它会在内存中创建一个类对象(注意,这里说的是类对象,不是实例!)。

class块运行时,所有定义的符号都属于这个类本身。这包括变量,比如你第一个例子中的eggs变量,还有你定义的__init____str__方法。所有这些在类定义时就被创建了,都是这个类的一部分。

实例变量直到你真正创建了对象的实例,并且运行了__init__方法时才会被创建,并且它们必须是self的属性。

所以,当Python解释器执行

class Spam(object):
    eggs = {}

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

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

之后,当它执行

spam1 = Spam()

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

实例本身作为self参数传入,任何你在上面定义的内容都只属于这个实例。这就是为什么self必须传入每个类方法的原因——在Python中,方法实际上属于类本身,而self是你唯一可以用来引用实例的方式。

7

那不是一个实例变量,而是一个类变量。通过实例来访问这个变量并不重要;它仍然是同一个对象。

8

正如Ignacio所说,在Python中,在类的范围内赋值的变量就是类变量。简单来说,Python中的类就是在一个class语句下的一系列语句。当这些语句执行完后,Python会把执行过程中创建的变量收集起来,形成一个类。如果你想要一个实例变量,你实际上需要把它赋值给那个实例。

另外,听起来你可能是从Java(或类似Java的语言)过来的。所以你可能知道,Java要求变量必须明确声明,因此需要在类的范围内声明实例变量。

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

注意,只有声明是在类的范围内。换句话说,为这个变量分配的内存是类的“模板”一部分,但变量的实际并不在这里。

Python不需要变量声明。所以在Python中,你只需省略声明部分。

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

内存会在需要的时候分配;实际上只有赋值的部分是写出来的。这部分会放在构造函数里,就像在Java中一样。

撰写回答