python:类属性和实例属性

时间:2011-09-25 20:56:32

标签: python

我是python的新手,并了解到类属性就像C ++中的静态数据成员一样。但是,在尝试以下代码后我感到困惑:

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

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

如果a被定义为列表而不是整数,则行为是预期的:

>>> 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,但它没有回答我的问题。

任何人都能解释为什么会有区别吗?感谢

6 个答案:

答案 0 :(得分:9)

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

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

当您分配到f1.a时,您要在f1的对象词典中添加一个条目。 f1.a的后续查找将找到该条目。 f2.a的查找仍然会找到class属性 - 类属性字典中的条目。

您可以将f1.a的值恢复为1,方法是删除它:

del f1.a

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

答案 1 :(得分:7)

>>> 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]

答案 2 :(得分: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

在这个例子中,我们实际上是在改变 class 属性的值, 并且该更改在该类的所有实例中都可见。当你 类型:

f1.a = 5

您使用实例属性覆盖属性。

答案 3 :(得分: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.a生成f1.a = 5之前执行的指令1? 那是因为:

  

一个类实例有一个名称实现为字典的名称空间   搜索属性引用的第一个位置。当一个   在那里找不到属性,并且实例的类有一个   通过该名称的属性,搜索继续该类   属性。

     

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

答案 4 :(得分:0)

以下是:

实例化新对象f1 = Foo()时,它没有自己的任何属性。每当您尝试访问f1.a时,您都会被重定向到班级的Foo.a

print f1.__dict__
{}

print f1.a
1

但是,如果设置 f1.a = 5,则实例会获取该值的新属性:

print f1.__dict__
{'a': 5}

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

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

答案 5 :(得分:0)

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

>>> f1.a=5

此处具有该属性的对象是实例,因此它是获取新值的实例。