2010-03-11 137 views
5

我一直在试图理解Python对类和实例变量的处理。特别是,我发现this answer相当有帮助。基本上它说,如果你声明一个类变量,然后你做了一个分配到[instance].property,你将分配给一个不同的变量 - 一个在不同的名字空间的类变量。对Python类和实例变量的复合赋值

于是我认为 - 如果我想我的类的每个实例有一些默认值的成员(说零),我应该做这样的:

class Foo: 
    num = 0 

或这样吗?

class Foo: 
    def __init__(self): 
     self.num = 0 

基于我早一点看,我认为第二个例子是初始化“正确”的变量(实例,而不是类变量)。但是,我发现第一种方法也可以很好地工作:

class Foo: 
    num = 0 

bar = Foo() 
bar.num += 1 # good, no error here, meaning that bar has an attribute 'num' 
bar.num 
>>> 1 
Foo.num 
>>> 0 # yet the class variable is not modified! so what 'num' did I add to just now? 

那么......为什么这样工作?我没有得到什么? FWIW,我之前对OOP的理解来自C++,所以用类比的方式来解释(或者指出它的分解点)可能是有用的。

回答

5

就我个人而言,我发现Shalabh Chaturvedi的these documents对此主题非常有用和信息丰富。

bar.num += 1bar.num = bar.num + 1的简写。这是拾取右侧的类变量Foo.num并将其分配给实例变量bar.num

+1

而当我说类/实例'变量'我真正的意思是'属性'。 – MattH 2010-03-11 11:56:12

0
bar.num += 1 

上的“棒”实例创建一个新的实例变量“NUM”,因为它不存在(然后加1这个值)

一个例子:

class Foo: 
    def __init__(self): 
    self.num= 1 

bar = Foo() 
print bar.num 

此打印1

print bar.foo 

这给预期的错误:回溯(最近通话最后一个): 文件 “”,1号线,在 AttributeError的:富实例没有属性 '富'

bar.foo = 5 
print bar.foo 

现在这个打印5

等什么在你的例子发生:bar.num解析为Foo。因为只有一个类属性。那么foo.num实际上是因为您为其赋值而创建的。

+0

呃......'bar.foo + = 1'也给出了一个错误,很明显Python不会在它遇到它时创建实例变量包含'+ ='的表达式。我的问题是,如何创建类变量导致对应的实例变量的初始化? – int3 2010-03-11 11:40:24

+1

@ int3。这与属性如何解决有关。当访问属性时,首先搜索对象(在这种情况下为'num'),然后搜索对象的类型('Foo'),然后搜索对象类型的基类和基类。如果没有找到匹配的属性,则引发'AttributeError'。确实是 – MattH 2010-03-11 11:54:04

+0

,然后创建实例变量是因为我们为它分配了一个值。我会更新示例 – 2010-03-11 11:58:43

-3

我想你刚在Python里发现了一个bug。 bar.num + = 1应该是一个错误,而是在对象栏中创建一个与Foo.num不同的属性num。

这是一个非常奇怪的行为。

+0

@Morgaelyn:不,这不是一个错误,这是预期的行为。阅读python类http://docs.python.org/tutorial/classes.html – MattH 2010-03-11 12:00:01

+0

正如MattH所说,这是有意和有记录的行为。所以,不,这不是一个错误。 – krawyoti 2010-03-11 13:15:01

+0

现在我明白了。 b.num + = 1与b.num = b.num + 1相同。第一个b.num引用对象属性并创建。第二个b.num实际上指的是Foo.num,它是0. NOW现在是合理的。我仍然认为这是该语言的设计错误。很容易假设b.num + = 1中的b.num始终指向相同的变量,但它实际上是指同时引用两个不相关的变量。 – 2010-03-11 13:27:54

2

在以下代码中,num是一个类成员。

class Foo: 
    num = 0 

A C++当量将像

struct Foo { 
    static int num; 
}; 

int Foo::num = 1; 

class Foo: 
    def __init__(self): 
     self.num = 0 

self.num是一个实例成员(self是的Foo一个实例)。

在C++中,它会像

struct Foo { 
    int num; 
}; 

我认为,Python允许你有一个类成员和实例成员共享相同的名称(C++不)。因此,当您执行bar = Foo()时,barFoo的实例,因此使用bar.num += 1时,您将增加实例成员。

+0

您的C++等价物似乎已将num的值递增1! :) – Kylotan 2010-03-11 12:02:17

0

寻找这个问题,这个StackOverflow问题和Guido van Rossum(1,2)的两张幻灯片(相当老,但是有效)都出现了很高。 Guido的幻灯片表明,这种行为与Python的访问属性的搜索顺序有关(在num以上的例子中)。我认为把这两者放在一起很好:)