今天我正在编写一个简单的脚本,当时我注意到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以这种方式工作。任何人都可以对此有所了解吗?
答案 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
是你必须引用实例的唯一方法。