我是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,但它没有回答我的问题。
任何人都能解释为什么会有区别吗?感谢
答案 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
此处具有该属性的对象是实例,因此它是获取新值的实例。