类属性和实例属性有什么区别?
这两种写法有什么实质性的区别吗:
class A(object):
foo = 5 # some default value
和
class B(object):
def __init__(self, foo=5):
self.foo = foo
如果你要创建很多实例,这两种写法在性能或占用空间上有什么不同吗?当你阅读代码时,你觉得这两种写法的含义有很大区别吗?
5 个回答
44
这里有一篇很不错的文章,下面是它的总结。
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 = Bar(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
还有一个图示
类属性的赋值
如果通过类来设置类属性,这个属性的值会覆盖所有实例的值。
foo = Bar(2) foo.class_var ## 1 Bar.class_var = 2 foo.class_var ## 2
如果通过实例来设置类变量,这个属性的值只会覆盖该实例的值。这实际上是将类变量覆盖成了实例变量,这样的话,这个变量就只对这个实例有效了。
foo = Bar(2) foo.class_var ## 1 foo.class_var = 2 foo.class_var ## 2 Bar.class_var ## 1
什么时候使用类属性呢?
存储常量。因为类属性可以作为类本身的属性来访问,所以通常用它们来存储类范围内的常量会比较方便。
class 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个项目。
class 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
45
区别在于,类上的属性是所有实例共享的,而实例上的属性是独一无二的,只属于那个特定的实例。
如果你是从C++过来的,类上的属性更像是静态成员变量。
171
这里有一个重要的语义区别(不仅仅是性能方面的考虑):
- 当属性是在实例上定义(这通常是我们做的),那么可以有多个对象被引用。每个对象都有自己独立的属性版本。
- 当属性是在类上定义,那么只有一个基础对象被引用,所以如果这个类的不同实例都试图设置(添加/扩展/插入等)这个属性,那么:
- 如果这个属性是内置类型(比如整数、浮点数、布尔值、字符串),那么一个对象上的操作会覆盖(冲掉)另一个对象的值。
- 如果这个属性是可变类型(比如列表或字典),那么我们会遇到不想要的“泄漏”问题。
举个例子:
>>> 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
[]