2011-09-25 66 views
6

我是python的新手,了解到类属性就像C++中的静态数据成员。然而,在尝试下面的代码后我感到困惑:python:class属性和实例属性

>>> class Foo: 
...  a=1 
... 
>>> f1=Foo(); 
>>> f2=Foo() 
>>> f1.a 
1 
>>> f1.a=5 
>>> f1.a 
5 
>>> f2.a 
1 

不应该f2.a也等于5吗?

如果被定义为一个列表,而不是一个整数,该行为是正常的:

>>> class Foo: 
...  a=[] 
... 
>>> f1=Foo(); 
>>> f2=Foo() 
>>> f1.a 
[] 
>>> f1.a.append(5) 
>>> f1.a 
[5] 
>>> f2.a 
[5] 

我看着 Python: Difference between class and instance attributes,但它并没有回答我的问题。

任何人都可以解释为什么区别?由于

+0

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

回答

7

你在第二个例子中没有做同样的事情。在你第一个例子中,你正在分配f1.a一个新值:

f1.a = 5 

在你的第二个例子,你只是延伸的名单:

f1.a.append(5) 

这不会改变什么f1.a指向。如果你是这样做的:

f1.a = [5] 

你会发现这与你的第一个例子相同。

但考虑这个例子:

>>> f1=Foo() 
>>> f2=Foo() 
>>> Foo.a = 5 
>>> f1.a 
5 
>>> f2.a 
5 

在这个例子中,我们实际上改变属性, 的价值的变化是在类的所有实例可见。当你 类型:

f1.a = 5 

你重写属性与实例属性。

+2

”您正在使用实例属性覆盖类属性。“我不这么认为。类属性保持不变。在用户11869的代码中,** a **的属性在** f1 **的namspace中由指令“f1.a = 5”创建,因为此属性以前不存在,因为它是在我的答案中显示使用** \ _ _ _ dict __ ** – eyquem

0

什么情况如下:

当你实例化一个新的对象f1 = Foo(),它没有任何自己的属性。当您尝试访问例如f1.a,您重定向到类的Foo.a

print f1.__dict__ 
{} 

print f1.a 
1 

但是,如果设置f1.a = 5,实例获取价值的一个新的属性:

print f1.__dict__ 
{'a': 5} 

类定义不受此影响,就像任何其他实例一样。

在第二个示例中,不要重新分配任何内容。有了append,您只能使用该类中定义的完全相同的列表。因此,您的实例仍然引用该列表,就像所有其他实例一样。

+1

与我即将输入的答案相同。我只是补充道,它与列表“起作用”的原因是因为在那种情况下对象是[mutable](http://docs.python.org/library/stdtypes.html#typesseq-mutable)。由于所有实例都指向同一个对象,因此所有对这个对象的修改都可以看到。 –

+0

@Debilski:新实例实际上经常具有它们自己的属性(您在回答中编写的'f1 .__ dict__'是其中的一个,可以看到比较'id(Foo.a)'和'id(f1 .a)中')。 “ – EOL

5
>>> class Foo: 
...  a=1 
... 
>>> f1=Foo() 
>>> f2=Foo() 
>>> f1.a # no instance attribute here in f1, so look up class attribute in Foo 
1 
>>> f1.a=5 # create new instance attribute in f1 
>>> f1.a # instance attribute here. Great, let's use this. 
5 
>>> f2.a # no instance attribute in f2, look up class attribute in Foo 
1 
>>> 
>>> class Foo: 
...  a=[] 
... 
>>> f1=Foo() 
>>> f2=Foo() 
>>> f1.a   # no instance attribute - look up class attribute in Foo 
[] 
>>> f1.a.append(5) # no instance attribute, modify class attribute in-place 
>>> f1.a   # no instance attribute - look up class attribute in Foo 
[5] 
>>> f2.a   # same here 
[5] 
0

在python中分配属性时,该属性可能已经定义的位置并不重要,新分配始终应用于分配给的对象。当你说

>>> f1.a=5 

在这里具有属性的对象是实例,所以它是获取新值的实例。

3

我的建议是:要了解这样的情况下,做用id__dict__过测试:

class Foo: 
    a=1 

# Foo.a == 1 
# id(Foo.a) == 10021840 
# Foo.__dict__ == {'a': 1, '__module__': '__main__', '__doc__': None} 


f1 = Foo() 

# f1.a == 1 
# id(f1.a) == 10021840 
# f1.__dict__ == {} 

f2 = Foo() 

# f2.a == 1 
# id(f2.a) == 10021840 
# f2.__dict__ == {} 

f1.a = 5 

# f1.a == 5 
# id(f1.a) == 10021792 
# f1.__dict__ == {'a': 5} 

# f2.a == 1 
# id(f2.a) == 10021840 
# f2.__dict__ == {} 

这表明,只要指令f1.a = 5还没有被执行,则实例f1没有个人属性a

那么,为什么指令print f1.af1.a = 5产生1之前执行?
这是因为:

一个类的实例有一个用字典是 在属性引用首位被搜索实现的名字空间。当在那里找不到 属性,并且该实例的类具有该名称的 属性时,搜索将继续使用类 属性。

http://docs.python.org/reference/datamodel.html#the-standard-type-hierarchy

9

Python的类的属性和对象的属性被存储在单独的dictionaries。对于对象f1,可以分别通过f1.__class__.__dict__f1.__dict__访问这些对象。执行print f1.__class__ is Foo将输出True

当您引用对象的属性时,Python首先会尝试在对象字典中查找它。如果它没有在那里找到它,它会检查类字典(等等继承heirarchy)。

当您分配给f1.a时,您正在向对象字典添加条目f1。随后查找f1.a将找到该条目。查找f2.a仍然会查找类属性 - 类属性字典中的条目。

可以导致f1.a值删除它恢复到1

del f1.a 

这将删除a项中的f1对象字典,并随后查找将继续以类字典。因此,之后,print f1.a将输出1.

+0

我觉得这个答案更详细 – Yuncy

+0

@Yuncy我认为相同 – eyquem