类属性和实例属性的区别是什么?

时间:2022-07-18 13:21:55

Is there any meaningful distinction between:

这两者之间有什么有意义的区别吗?

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

vs.

vs。

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

If you're creating a lot of instances, is there any difference in performance or space requirements for the two styles? When you read the code, do you consider the meaning of the two styles to be significantly different?

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

6 个解决方案

#1


133  

Beyond performance considerations, there is a significant semantic difference. In the class attribute case, there is just one object referred to. In the instance-attribute-set-at-instantiation, there can be multiple objects referred to. For instance

除了性能考虑之外,还有显著的语义差异。在类属性案例中,只有一个对象被引用。在实例-属性-集-实例化中,可以引用多个对象。例如

>>> 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    
[]

#2


31  

The difference is that the attribute on the class is shared by all instances. The attribute on an instance is unique to that instance.

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

If coming from C++, attributes on the class are more like static member variables.

如果来自c++,类上的属性更像静态成员变量。

#3


22  

Here is a very good post, and summary it as below.

这里有一个很好的帖子,总结如下。

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

And in visual form

而在视觉形式

类属性和实例属性的区别是什么?

Class attribute assignment

类属性赋值

  • If a class attribute is set by accessing the class, it will override the value for all instances

    如果类属性是通过访问类设置的,它将覆盖所有实例的值。

    foo = Bar(2)
    foo.class_var
    ## 1
    Bar.class_var = 2
    foo.class_var
    ## 2
    
  • If a class variable is set by accessing an instance, it will override the value only for that instance. This essentially overrides the class variable and turns it into an instance variable available, intuitively, only for that instance.

    如果一个类变量是通过访问一个实例来设置的,那么它只会覆盖那个实例的值。这实际上覆盖了类变量,并将其转换为可用的实例变量,直观地,仅针对该实例。

    foo = Bar(2)
    foo.class_var
    ## 1
    foo.class_var = 2
    foo.class_var
    ## 2
    Bar.class_var
    ## 1
    

When would you use class attribute?

什么时候使用class属性?

  • Storing constants. As class attributes can be accessed as attributes of the class itself, it’s often nice to use them for storing Class-wide, Class-specific constants

    存储常数。由于类属性可以作为类本身的属性来访问,因此使用它们来存储类范围的、类特定的常量通常是很好的

    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
    
  • Defining default values. As a trivial example, we might create a bounded list (i.e., a list that can only hold a certain number of elements or fewer) and choose to have a default cap of 10 items

    定义默认值。作为一个简单的例子,我们可以创建一个有界列表(例如。,一个只能包含一定数量或更少元素的列表),并选择拥有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


18  

Since people in the comments here and in two other questions marked as dups all appear to be confused about this in the same way, I think it's worth adding an additional answer on top of Alex Coventry's.

既然在这里的评论和另外两个被标记为dups的问题中的人们似乎都以同样的方式对此感到困惑,我认为在Alex Coventry的基础上增加一个额外的答案是值得的。

The fact that Alex is assigning a value of a mutable type, like a list, has nothing to do with whether things are shared or not. We can see this with the id function or the is operator:

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

(If you're wondering why I used object() instead of, say, 5, that's to avoid running into two whole other issues which I don't want to get into here; for two different reasons, entirely separately-created 5s can end up being the same instance of the number 5. But entirely separately-created object()s cannot.)

如果你想知道为什么我用object()而不是,比如说,5,这是为了避免遇到另外两个问题我不想在这里讲;由于两个不同的原因,完全独立创建的5s最终可能成为数字5的相同实例。但是完全独立创建的对象()不能。


So, why is it that a.foo.append(5) in Alex's example affects b.foo, but a.foo = 5 in my example doesn't? Well, try a.foo = 5 in Alex's example, and notice that it doesn't affect b.foo there either.

那么,为什么Alex的例子中的a.foo.append(5)会影响b。foo,但是。我的例子中foo = 5不是吗?好吧,试一试。在Alex的例子中foo = 5,注意它不影响b。foo。

a.foo = 5 is just making a.foo into a name for 5. That doesn't affect b.foo, or any other name for the old value that a.foo used to refer to.* It's a little tricky that we're creating an instance attribute that hides a class attribute,** but once you get that, nothing complicated is happening here.

一个。foo = 5是a。foo为5的名称。这并不影响b。foo,或a的旧值的任何其他名称。foo用来表示。我们创建一个隐藏类属性的实例属性有点棘手,但是一旦你得到它,这里就不会发生什么复杂的事情了。


Hopefully it's now obvious why Alex used a list: the fact that you can mutate a list means it's easier to show that two variables name the same list, and also means it's more important in real-life code to know whether you have two lists or two names for the same list.

希望现在明显的亚历克斯为什么使用列表:可以改变这一事实意味着更容易显示出两个变量列表名称相同的列表,也意味着更重要的在现实生活中的代码,知道你有两个列表或两个名称相同的列表。


* The confusion for people coming from a language like C++ is that in Python, values aren't stored in variables. Values live off in value-land, on their own, variables are just names for values, and assignment just creates a new name for a value. If it helps, think of each Python variable as a shared_ptr<T> instead of a T.

*来自c++等语言的人感到困惑的是,在Python中,值不存储在变量中。值依赖于值域,仅凭它们自己,变量只是值的名称,赋值只是为值创建一个新名称。如果有帮助,可以将每个Python变量看作shared_ptr 而不是T。

** Some people take advantage of this by using a class attribute as a "default value" for an instance attribute that instances may or may not set. This can be useful in some cases, but it can also be confusing, so be careful with it.

**有些人利用这个优势,将类属性作为实例属性的“默认值”,实例属性可以设置也可以不设置。

#5


2  

Just an elaboration on what Alex Coventry said, another Alex (Martelli) addressed a similar question on the comp.lang.python newsgroup years back. He examines the semantic difference of what a person intended vs. what he got (by using instance variables).

只是对亚历克斯·考文垂的话进行了阐述,另一个亚历克斯(马特里饰)在节目中也提出了类似的问题。python新闻组。他通过使用实例变量来检验一个人想要的和得到的语义差异。

http://groups.google.com/group/comp.lang.python/msg/5914d297aff35fae?hl=en

http://groups.google.com/group/comp.lang.python/msg/5914d297aff35fae?hl=en

#6


0  

There is one more situation.

还有一种情况。

Class and instance attributes is Descriptor.

类和实例属性是描述符。

# -*- 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()

Above will output:

以上将输出:

('Access to class attribute, return: ', 10)
('Access to instance attribute, return: ', <__main__.RevealAccess object at 0x10184eb50>)

The same type of instance access through class or instance return different result!

相同类型的实例访问通过类或实例返回不同的结果!

And i found in c.PyObject_GenericGetAttr definition,and a great post.

我发现了c。PyObject_GenericGetAttr定义,和一个伟大的职位。

Explain

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).

如果在组成类的字典中找到属性。对象MRO,然后检查属性是否被查找到数据描述符(这并不是一个实现__get__和__set__方法的类)。如果有,通过调用数据描述符的__get__方法(第28-33行)来解析属性查找。

#1


133  

Beyond performance considerations, there is a significant semantic difference. In the class attribute case, there is just one object referred to. In the instance-attribute-set-at-instantiation, there can be multiple objects referred to. For instance

除了性能考虑之外,还有显著的语义差异。在类属性案例中,只有一个对象被引用。在实例-属性-集-实例化中,可以引用多个对象。例如

>>> 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    
[]

#2


31  

The difference is that the attribute on the class is shared by all instances. The attribute on an instance is unique to that instance.

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

If coming from C++, attributes on the class are more like static member variables.

如果来自c++,类上的属性更像静态成员变量。

#3


22  

Here is a very good post, and summary it as below.

这里有一个很好的帖子,总结如下。

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

And in visual form

而在视觉形式

类属性和实例属性的区别是什么?

Class attribute assignment

类属性赋值

  • If a class attribute is set by accessing the class, it will override the value for all instances

    如果类属性是通过访问类设置的,它将覆盖所有实例的值。

    foo = Bar(2)
    foo.class_var
    ## 1
    Bar.class_var = 2
    foo.class_var
    ## 2
    
  • If a class variable is set by accessing an instance, it will override the value only for that instance. This essentially overrides the class variable and turns it into an instance variable available, intuitively, only for that instance.

    如果一个类变量是通过访问一个实例来设置的,那么它只会覆盖那个实例的值。这实际上覆盖了类变量,并将其转换为可用的实例变量,直观地,仅针对该实例。

    foo = Bar(2)
    foo.class_var
    ## 1
    foo.class_var = 2
    foo.class_var
    ## 2
    Bar.class_var
    ## 1
    

When would you use class attribute?

什么时候使用class属性?

  • Storing constants. As class attributes can be accessed as attributes of the class itself, it’s often nice to use them for storing Class-wide, Class-specific constants

    存储常数。由于类属性可以作为类本身的属性来访问,因此使用它们来存储类范围的、类特定的常量通常是很好的

    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
    
  • Defining default values. As a trivial example, we might create a bounded list (i.e., a list that can only hold a certain number of elements or fewer) and choose to have a default cap of 10 items

    定义默认值。作为一个简单的例子,我们可以创建一个有界列表(例如。,一个只能包含一定数量或更少元素的列表),并选择拥有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


18  

Since people in the comments here and in two other questions marked as dups all appear to be confused about this in the same way, I think it's worth adding an additional answer on top of Alex Coventry's.

既然在这里的评论和另外两个被标记为dups的问题中的人们似乎都以同样的方式对此感到困惑,我认为在Alex Coventry的基础上增加一个额外的答案是值得的。

The fact that Alex is assigning a value of a mutable type, like a list, has nothing to do with whether things are shared or not. We can see this with the id function or the is operator:

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

(If you're wondering why I used object() instead of, say, 5, that's to avoid running into two whole other issues which I don't want to get into here; for two different reasons, entirely separately-created 5s can end up being the same instance of the number 5. But entirely separately-created object()s cannot.)

如果你想知道为什么我用object()而不是,比如说,5,这是为了避免遇到另外两个问题我不想在这里讲;由于两个不同的原因,完全独立创建的5s最终可能成为数字5的相同实例。但是完全独立创建的对象()不能。


So, why is it that a.foo.append(5) in Alex's example affects b.foo, but a.foo = 5 in my example doesn't? Well, try a.foo = 5 in Alex's example, and notice that it doesn't affect b.foo there either.

那么,为什么Alex的例子中的a.foo.append(5)会影响b。foo,但是。我的例子中foo = 5不是吗?好吧,试一试。在Alex的例子中foo = 5,注意它不影响b。foo。

a.foo = 5 is just making a.foo into a name for 5. That doesn't affect b.foo, or any other name for the old value that a.foo used to refer to.* It's a little tricky that we're creating an instance attribute that hides a class attribute,** but once you get that, nothing complicated is happening here.

一个。foo = 5是a。foo为5的名称。这并不影响b。foo,或a的旧值的任何其他名称。foo用来表示。我们创建一个隐藏类属性的实例属性有点棘手,但是一旦你得到它,这里就不会发生什么复杂的事情了。


Hopefully it's now obvious why Alex used a list: the fact that you can mutate a list means it's easier to show that two variables name the same list, and also means it's more important in real-life code to know whether you have two lists or two names for the same list.

希望现在明显的亚历克斯为什么使用列表:可以改变这一事实意味着更容易显示出两个变量列表名称相同的列表,也意味着更重要的在现实生活中的代码,知道你有两个列表或两个名称相同的列表。


* The confusion for people coming from a language like C++ is that in Python, values aren't stored in variables. Values live off in value-land, on their own, variables are just names for values, and assignment just creates a new name for a value. If it helps, think of each Python variable as a shared_ptr<T> instead of a T.

*来自c++等语言的人感到困惑的是,在Python中,值不存储在变量中。值依赖于值域,仅凭它们自己,变量只是值的名称,赋值只是为值创建一个新名称。如果有帮助,可以将每个Python变量看作shared_ptr 而不是T。

** Some people take advantage of this by using a class attribute as a "default value" for an instance attribute that instances may or may not set. This can be useful in some cases, but it can also be confusing, so be careful with it.

**有些人利用这个优势,将类属性作为实例属性的“默认值”,实例属性可以设置也可以不设置。

#5


2  

Just an elaboration on what Alex Coventry said, another Alex (Martelli) addressed a similar question on the comp.lang.python newsgroup years back. He examines the semantic difference of what a person intended vs. what he got (by using instance variables).

只是对亚历克斯·考文垂的话进行了阐述,另一个亚历克斯(马特里饰)在节目中也提出了类似的问题。python新闻组。他通过使用实例变量来检验一个人想要的和得到的语义差异。

http://groups.google.com/group/comp.lang.python/msg/5914d297aff35fae?hl=en

http://groups.google.com/group/comp.lang.python/msg/5914d297aff35fae?hl=en

#6


0  

There is one more situation.

还有一种情况。

Class and instance attributes is Descriptor.

类和实例属性是描述符。

# -*- 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()

Above will output:

以上将输出:

('Access to class attribute, return: ', 10)
('Access to instance attribute, return: ', <__main__.RevealAccess object at 0x10184eb50>)

The same type of instance access through class or instance return different result!

相同类型的实例访问通过类或实例返回不同的结果!

And i found in c.PyObject_GenericGetAttr definition,and a great post.

我发现了c。PyObject_GenericGetAttr定义,和一个伟大的职位。

Explain

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).

如果在组成类的字典中找到属性。对象MRO,然后检查属性是否被查找到数据描述符(这并不是一个实现__get__和__set__方法的类)。如果有,通过调用数据描述符的__get__方法(第28-33行)来解析属性查找。