之间的区别是什么类和实例属性?
-
03-07-2019 - |
题
是否有任何有意义的区分:
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 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
,那就是为了避免遇到其他两个我不想进入的问题;对于两个不同的问题原因,完全单独创建的a.foo.append(5)
s最终可能是数字b.foo
的同一个实例。但是完全单独创建的a.foo = 5
s不能。)
那么,为什么Alex的例子中的a.foo
会影响shared_ptr<T>
,但T
在我的例子中却没有?好吧,在Alex的例子中尝试<=>,并注意它不会影响<=>那里 。
<=>只是将<=>转换为<=>的名称。这不会影响<=>,或<=>过去引用的旧值的任何其他名称。*我们创建一个隐藏类属性的实例属性有点棘手,**但是一旦你知道了,这里没有什么复杂的事情发生。
希望现在很明显为什么Alex使用了一个列表:事实上你可以改变列表意味着更容易显示两个变量命名相同的列表,这也意味着在现实代码中更重要的是知道你是否有两个列表或同一列表中的两个名称。
*来自像C ++这样的语言的人的困惑在于,在Python中,值不存储在变量中。值存在于value-land中,变量只是值的名称,赋值只是为值创建一个新名称。如果有帮助,可以将每个Python变量视为<=>而不是<=>。
**有些人通过将类属性用作<!>“默认值<!>”来利用此功能。对于实例可能设置或可能未设置的实例属性。这在某些情况下很有用,但也可能令人困惑,所以要小心。
还有一种情况。
类和实例属性是描述符。
# -*- 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()
以上将输出:
('Access to class attribute, return: ', 10)
('Access to instance attribute, return: ', <__main__.RevealAccess object at 0x10184eb50>)
通过类或实例访问相同类型的实例会返回不同的结果!
我在 c.PyObject_GenericGetAttr定义 <!>#65292;还有一个很好的帖子。
解释
如果在组成的类的字典中找到该属性。 对象MRO,然后检查查找的属性是否指向数据描述符(这不过是实现
__get__
和__set__
方法的类)。 如果是,请通过调用数据描述符的<=>方法来解析属性查找(第28行<!>#8211; 33)。