当前位置:  开发笔记 > 编程语言 > 正文

类和实例属性有什么区别?

如何解决《类和实例属性有什么区别?》经验,为你挑选了4个好方法。

是否有任何有意义的区别:

class A(object):
    foo = 5   # some default value

class B(object):
    def __init__(self, foo=5):
        self.foo = foo

如果您要创建大量实例,那么这两种样式的性能或空间要求是否存在差异?当您阅读代码时,您是否认为两种样式的含义有显着差异?



1> Alex Coventr..:

除了性能考虑之外,还存在显着的语义差异.在类属性的情况下,只有一个对象被引用.在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    
[]


但是,请注意,如果你执行`a.foo = 5`,那么在这两种情况下你都会看到`b.foo`返回`[]`.这是因为在第一种情况下,您使用相同名称的新实例属性覆盖类属性`a.foo`.
@Babu:不,`int`和`str`是_also_共享,完全一样.您可以使用`is`或`id`轻松检查.或者只查看每个实例的`__dict__`和类的`__dict__`.通常,无论是否共享不可变类型通常都无关紧要.
只共享可变类型.就像`int`和`str`一样,它们仍然依附于每个实例而不是类.

2> Peter Shinne..:

不同之处在于类的属性由所有实例共享.实例上的属性对于该实例是唯一的.

如果来自C++,则类上的属性更像是静态成员变量.


@Rafe:不,所有类型都是共享的.你感到困惑的原因是`a.foo.append(5)`正在改变'a.foo`所指的值,而`a.foo = 5`正在把'a.foo`变成一个新的值"5"的名称.因此,您最终会得到一个隐藏class属性的实例属性.在Alex的版本中尝试相同的`a.foo = 5`,你会看到`b.foo`没有变化.

3> zangw..:

这是一篇非常好的帖子,总结如下.

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



4> abarnert..:

由于这里的评论以及另外两个标记为重复的问题的人似乎都以同样的方式对此感到困惑,我认为值得在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那是为了避免遇到两个我不想进入的其他问题;由于两个不同的原因,完全单独创建的5s最终会成为相同的数字实例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.fooa.foo过去引用的旧值的任何其他名称.*我们创建一个隐藏类属性的实例属性有点棘手,但是一旦你得到它,没有什么复杂的是发生在这里.


希望现在很明显为什么Alex使用了一个列表:事实上你可以改变列表意味着更容易显示两个变量命名相同的列表,并且还意味着在现实代码中更重要的是知道你是否有两个列表或者同一个列表的两个名称.


*来自像C++这样的语言的人的困惑在于,在Python中,值不存储在变量中.值存在于value-land中,变量只是值的名称,赋值只是为值创建一个新名称.如果它有帮助,可以将每个Python变量视为一个shared_ptr而不是一个T.

**有些人通过使用类属性作为实例可能设置或可能未设置的实例属性的"默认值"来利用此功能.这在某些情况下很有用,但也可能令人困惑,所以要小心.

推荐阅读
ERIK又
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有