2012-01-14 70 views
6

今天我正在研究一个简单的脚本,当我注意到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 

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

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会以这种方式工作。任何人都可以对此有所了解吗?

+0

可能的重复[如何避免在实例间共享Python类数据?](http://stackoverflow.com/questions/1680528/how-do-i-avoid-having-python-class-data-shared -among-instances) – 2013-02-23 15:26:10

回答

7

正如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中一样。

+0

是的,正是 - 当我在大学攻读OOP时,我们使用了Java。 – 2012-01-14 06:15:06

5

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

+0

我明白你的意思了。但是,在定义类时,如何将变量声明为实例变量或类变量?我的意思是当你看这段代码时,你怎么知道它是一个类变量? – Divya 2012-01-14 05:59:39

+2

它是类定义的一部分,即'Spam.eggs'。当绑定为“self”的属性时,它不是类的属性,而是实例的属性。 – 2012-01-14 06:00:50

+0

好的,如果我正确理解了你的话,一旦我创建了一个垃圾邮件实例,我可以直接调用Spam.eggs并获取我刚创建的垃圾邮件实例的蛋?谢谢你的帮助! – 2012-01-14 06:07:08

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__方法。方法本身当然属于这个类;它在所有实例之间共享,就像eggs属性一样。

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