为什么Python的实例变量似乎在对象间共享?
今天我在写一个简单的脚本时,发现了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 个回答
和编译语言不同,在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
是你唯一可以用来引用实例的方式。
那不是一个实例变量,而是一个类变量。通过实例来访问这个变量并不重要;它仍然是同一个对象。
正如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中一样。