2014-03-27 69 views
2

为什么下面的例子表现不同?Python中的静态类变量和`self`

实施例1:foo似乎表现得象一个类变量的特定于各种对象

class A: 
    foo = 1 
a, b = A(), A() 
a.foo = 5 
print b.foo 
---------------- 
Output: 1 

实施例2:foo似乎表现得象一个静态类变量,它是对所有对象是相同的。也许这种行为与作为指针工作的列表有关。

class A: 
    foo = [] 
a, b = A(), A() 
a.foo.append(5) 
print b.foo 
---------------- 
Output: [5] 

实施例3:不工作

class A: 
    self.foo = [] 
a, b = A(), A() 
a.foo.append(5) 
print b.foo 
---------------- 
Output: Error 
+0

不要忘记接受,+2代表你! :) –

回答

5

前两个例子都是类属性。他们看起来不同的原因是因为在两种情况下你都没有做同样的事情:你在第一种情况下分配一个新值并在第二种情况下修改现有值。

请注意,您在前两个示例中没有做同样的事情。在第一个例子中,你做a.foo = 5,分配一个新的值。在第二个例子中,如果你做了类似的事情,分配a.foo = [5],你会看到与第一个例子中相同的结果。但是,您改变了现有的名单a.foo.append(5),所以行为是不同的。 a.foo = 5仅更改变量(即,它指向什么值); a.foo.append(5)更改本身。

(请注意,在第一个例子中没有办法做第二个例子的等价物,也就是说,没有任何东西像a.foo.add(1)加1到5那是因为整数是不可变的,但是列表是。没有列出“是”可变的,但你突变之一。换句话说,这不要紧,你可以什么用列表,它重要的是你在特定的代码实际上做的。)

另请注意,尽管在类定义中定义的foo是类属性,但在执行a.foo = 5时,您正在实例上创建新属性。它恰好与类属性具有相同的名称,但它不会更改class属性的值,这是b.foo仍然可以看到的值。

最后一个例子不起作用,因为就像在前两个例子中一样,class块内的代码位于类作用域。没有self,因为在定义类时还没有实例。

在StackOverflow上有很多关于这个问题的其他许多问题,我敦促您搜索并阅读其中的一堆,以更全面地了解它的工作原理。

+0

我明白了例3和'self'。 'self'作为我们定义的类的实际对象,所以在这一点上没有任何对象。但是,我没有看到前两个例子的区别。我显然可以改变整数的内容,因为在第一个例子中打印a.foo = 5。你能澄清一点吗? – Konstantin

+1

@Konstantin:这不会改变整数*值*,它正在改变*变量*指向的内容。请参阅[本页](http://foobarnbaz.com/2012/07/08/understanding-python-variables/),了解变量是否在Python中的简单解释。 – BrenBarn

+1

“他们看起来不同的原因是因为您可以更改列表的内容,但不能更改整数的内容。”不。他们所不同的是,1)涉及重新分配和2)就地变更(当然,这只能在可变对象中才能实现)。你在下一段中说清楚了,但第一个可能有点误导。 – glglgl

1

这不起作用:

class A: 
    self.foo = [] 

这引发了一个错误。

NameError: name 'self' is not defined 

因为self并不是一个Python关键字,它只是共同转让给传递到类的方法,当类被称为类的实例变量名。

下面是一个例子:

class A(object): 
    def __init__(self): 
     self.foo = [] 

a, b = A(), A() 
a.foo.append(5) 
print(b.foo) 

然后返回:

[] 

当每一个被初始化,他们各自获得自己的列表,它可以通过属性foo访问,并且当一个人修改,另一个是存储在内存中不同位置的单独列表,不受影响。

+1

亚伦,非常好的解释!挣扎了一下,因为我开始使用Python而不是C/C++做一些东西 – Konstantin

+1

@Konstantin感谢您的认可。 :)我正要离开布伦,但我想我有些东西可以补充他。 –

1

区别与可变性/不可变性,但执行什么操作。

在示例1中,该类具有属性foo。创建对象之后,您会为对象提供另一个属性foo,该属性会遮蔽前一个。所以class属性可以作为一种“默认”或“后备”。

在示例2中,您有一个对象执行操作(当然,它只适用于可变对象)。因此,由A.foo引用的对象也可以通过a.foob.foo访问,由于缺少具有相同名称的实例属性,所以添加了5

示例3不起作用,因为在您使用它的地方不存在self

注意,例如1人以及与可变对象,如列表工作:

class A: 
    foo = [] 
a, b = A(), A() 
a.foo = [] 
a.foo.append(5) 
b.foo.append(10) 
print a.foo # [5] 
print b.foo # [10] 
print A.foo # [10] 

这里a.foo得到一个新的空单。缺少实例属性的b.foo继续引用类属性。所以我们有两个相互独立的空列表,就像我们在.append() ing时看到的那样。

+0

有关示例1中的“fallback”属性的好处,因此它的作用类似于类的静态成员,同时也是实例的默认属性。当你像'A.foo = 7'那样将其改变为类的静态变量时,默认属性会改变 – Konstantin