What is the difference between class and instance attributes?
以下两者之间是否存在任何有意义的区别:
1 2 | class A(object): foo = 5 # some default value |
VS
1 2 3 | class B(object): def __init__(self, foo=5): self.foo = foo |
如果您要创建很多实例,那么这两种样式在性能或空间要求上有什么不同吗?当您阅读代码时,您是否认为这两种样式的含义有显著差异?
除了性能考虑之外,还有一个显著的语义差异。在类属性的情况下,只有一个对象被引用。在实例化时设置的实例属性中,可以引用多个对象。例如
1 2 3 4 5 6 7 8 9 10 11 | >>> class A: foo = [] >>> a, b = A(), A() >>> a.foo.append(5) >>> b.foo [5] >>> class A: ... def __init__(self): self.foo = [] >>> a, b = A(), A() >>> a.foo.append(5) >>> b.foo [] |
区别在于类上的属性由所有实例共享。实例的属性对于该实例是唯一的。
如果来自C++,则类上的属性更像静态成员变量。
这是一篇很好的文章,总结如下。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | class Bar(object): ## No need for dot syntax class_var = 1 def __init__(self, i_var): self.i_var = i_var ## Need dot syntax as we've left scope of class namespace Bar.class_var ## 1 foo = MyClass(2) ## Finds i_var in foo's instance namespace foo.i_var ## 2 ## Doesn't find class_var in instance namespace… ## So look's in class namespace (Bar.__dict__) foo.class_var ## 1 |
以视觉形式
类属性分配
如果通过访问类来设置类属性,它将覆盖所有实例的值。
1
2
3
4
5
6foo = Bar(2)
foo.class_var
## 1
Bar.class_var = 2
foo.class_var
## 2如果通过访问实例来设置类变量,它将仅覆盖该实例的值。这基本上覆盖了类变量,并将其转换为一个仅对该实例可用的实例变量。
1
2
3
4
5
6
7
8foo = Bar(2)
foo.class_var
## 1
foo.class_var = 2
foo.class_var
## 2
Bar.class_var
## 1
什么时候使用类属性?
存储常量。由于类属性可以作为类本身的属性来访问,因此通常最好使用它们来存储类范围内特定于类的常量。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15class Circle(object):
pi = 3.14159
def __init__(self, radius):
self.radius = radius
def area(self):
return Circle.pi * self.radius * self.radius
Circle.pi
## 3.14159
c = Circle(10)
c.pi
## 3.14159
c.area()
## 314.159定义默认值。作为一个简单的例子,我们可以创建一个有界列表(即只能容纳一定数量的元素或更少元素的列表),并选择默认值为10个项目的上限。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15class MyClass(object):
limit = 10
def __init__(self):
self.data = []
def item(self, i):
return self.data[i]
def add(self, e):
if len(self.data) >= self.limit:
raise Exception("Too many elements")
self.data.append(e)
MyClass.limit
## 10
由于这里的评论和另外两个被标记为dup的问题中的人似乎都以同样的方式对此感到困惑,我认为值得在alex coventry的基础上再加一个答案。
事实上,亚历克斯正在分配一个可变类型的值,比如一个列表,这与事物是否共享无关。我们可以通过
1 2 3 4 5 6 7 8 9 | >>> class A: foo = object() >>> a, b = A(), A() >>> a.foo is b.foo True >>> class A: ... def __init__(self): self.foo = object() >>> a, b = A(), A() >>> a.foo is b.foo False |
(如果您想知道为什么我使用
那么,为什么在亚历克斯的例子中,
希望亚历克斯使用列表的原因现在已经很明显了:事实上,你可以改变一个列表,这意味着更容易显示两个变量命名同一个列表,也意味着在现实代码中更重要的是要知道你有两个列表还是两个名称命名同一个列表。
*-来自C++语言的人的困惑是,在Python中,值不存储在变量中。价值生活在有价值的土地上,变量本身就是价值的名称,而赋值只是为价值创建一个新的名称。如果有帮助,可以将每个python变量都看作一个
**有些人利用这一点,使用类属性作为实例属性的"默认值",实例可以设置也可以不设置。这在某些情况下可能很有用,但也可能令人困惑,因此请小心。
仅仅是对亚历克斯考文垂所说的一个阐述,另一个亚历克斯(马泰利)多年前在
http://groups.google.com/group/comp.lang.python/msg/5914d297aff35fae?HL=EN
还有一种情况。
类和实例属性是描述符。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | # -*- encoding: utf-8 -*- class RevealAccess(object): def __init__(self, initval=None, name='var'): self.val = initval self.name = name def __get__(self, obj, objtype): return self.val class Base(object): attr_1 = RevealAccess(10, 'var"x"') def __init__(self): self.attr_2 = RevealAccess(10, 'var"x"') def main(): b = Base() print("Access to class attribute, return:", Base.attr_1) print("Access to instance attribute, return:", b.attr_2) if __name__ == '__main__': main() |
以上将输出:
1 2 | ('Access to class attribute, return: ', 10) ('Access to instance attribute, return: ', <__main__.RevealAccess object at 0x10184eb50>) |
通过类或实例访问相同类型的实例返回不同的结果!
我在c.pyobject的genericgetattr定义中找到了一个很好的帖子。
解释If the attribute is found in the dictionary of the classes which make up.
the objects MRO, then check to see if the attribute being looked up points to a Data Descriptor (which is nothing more that a class implementing both the__get__ and the__set__ methods).
If it does, resolve the attribute lookup by calling the__get__ method of the Data Descriptor (lines 28–33).