是否有任何有意义的区别:
class A(object): foo = 5 # some default value
与
class B(object): def __init__(self, foo=5): self.foo = foo
如果您要创建大量实例,那么这两种样式的性能或空间要求是否存在差异?当您阅读代码时,您是否认为两种样式的含义有显着差异?
除了性能考虑之外,还存在显着的语义差异.在类属性的情况下,只有一个对象被引用.在instance-attribute-set-at-instantiation中,可以引用多个对象.例如
>>> 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++,则类上的属性更像是静态成员变量.
这是一篇非常好的帖子,总结如下.
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
并以视觉形式
类属性赋值
如果通过访问类来设置类属性,它将覆盖所有实例的值
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属性?
存储常量.由于类属性可以作为类本身的属性进行访问,因此使用它们来存储类范围的特定于类的常量通常很好.
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
由于这里的评论以及另外两个标记为重复的问题的人似乎都以同样的方式对此感到困惑,我认为值得在Alex Coventry的基础上添加额外的答案.
Alex正在分配一个可变类型的值,如列表,这与事物是否共享无关.我们可以通过id
函数或is
运算符看到这个:
>>> 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
(如果你想知道为什么我用object()
而不是说,5
那是为了避免遇到两个我不想进入的其他问题;由于两个不同的原因,完全单独创建的5
s最终会成为相同的数字实例5
.但完全单独创建的object()
s不能.)
那么,为什么a.foo.append(5)
在Alex的例子中会影响b.foo
,但a.foo = 5
在我的例子中却没有?那么,尝试a.foo = 5
在Alex的例子,并注意不影响b.foo
有两种.
a.foo = 5
只是a.foo
为了一个名字5
.这不会影响b.foo
或a.foo
过去引用的旧值的任何其他名称.*我们创建一个隐藏类属性的实例属性有点棘手,但是一旦你得到它,没有什么复杂的是发生在这里.
希望现在很明显为什么Alex使用了一个列表:事实上你可以改变列表意味着更容易显示两个变量命名相同的列表,并且还意味着在现实代码中更重要的是知道你是否有两个列表或者同一个列表的两个名称.
*来自像C++这样的语言的人的困惑在于,在Python中,值不存储在变量中.值存在于value-land中,变量只是值的名称,赋值只是为值创建一个新名称.如果它有帮助,可以将每个Python变量视为一个shared_ptr
而不是一个T
.
**有些人通过使用类属性作为实例可能设置或可能未设置的实例属性的"默认值"来利用此功能.这在某些情况下很有用,但也可能令人困惑,所以要小心.