简体   繁体   English

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

[英]What is the difference between class and instance attributes?

Is there any meaningful distinction between:之间是否有任何有意义的区别:

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

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?当你阅读代码时,你是否认为两种样式的含义有显着不同?

There is a significant semantic difference (beyond performance considerations):存在显着的语义差异(超出性能考虑):

  • when the attribute is defined on the instance (which is what we usually do), there can be multiple objects referred to.当在实例上定义属性时(这是我们通常所做的),可以引用多个对象。 Each gets a totally separate version of that attribute .每个都获得该属性的完全独立的版本
  • when the attribute is defined on the class , there is only one underlying object referred to, so if operations on different instances of that class both attempt to set/(append/extend/insert/etc.) the attribute, then:当在类上定义属性时,只有一个底层对象被引用,因此如果对该类的不同实例的操作都尝试设置/(追加/扩展/插入/等)属性,则:
    • if the attribute is a builtin type (like int, float, boolean, string), operations on one object will overwrite (clobber) the value如果属性是内置类型(如 int、float、boolean、string),对一个对象的操作将覆盖(破坏)该值
    • if the attribute is a mutable type (like a list or a dict), we will get unwanted leakage.如果属性是可变类型(如列表或字典),我们将获得不必要的泄漏。

For example:例如:

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

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++,类的属性更像是静态成员变量。

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 (ie, 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

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 .由于此处的评论和其他两个标记为重复的问题中的人们似乎都对此感到困惑,因此我认为值得在亚历克斯考文垂的.

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. Alex 正在分配一个可变类型的值,如列表,这一事实与事物是否共享无关。 We can see this with the id function or the is operator:我们可以通过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 5 s can end up being the same instance of the number 5 . But entirely separately-created object() s cannot.) (如果您想知道为什么我使用object()而不是5 ,那是为了避免遇到我不想在这里讨论的两个其他问题;出于两个不同的原因,完全单独创建5 s最终可能是数字5的同一个实例。但完全独立创建的object()不能。)


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?那么,为什么 Alex 示例中的a.foo.append(5)会影响b.foo ,而我示例中的a.foo = 5却不会? Well, try a.foo = 5 in Alex's example, and notice that it doesn't affect b.foo there either .那么,尝试a.foo = 5在Alex的例子,并注意不影响b.foo两种

a.foo = 5 is just making a.foo into a name for 5 . a.foo = 5只是让a.foo成为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.这不会影响b.fooa.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.希望 Alex 使用列表的原因现在已经很明显了:你可以改变列表的事实意味着更容易显示两个变量命名同一个列表,也意味着在现实生活中的代码中更重要的是知道你是否有两个列表或同一个列表的两个名称。


* The confusion for people coming from a language like C++ is that in Python, values aren't stored in variables. * 来自 C++ 等语言的人的困惑在于,在 Python 中,值不存储在变量中。 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 .如果有帮助,请将每个 Python 变量视为shared_ptr<T>而不是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.这在某些情况下很有用,但也可能令人困惑,所以要小心。

There is one more situation.还有一种情况。

Class and instance attributes is Descriptor .类和实例属性是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).对象 MRO,然后检查正在查找的属性是否指向数据描述符(这只不过是实现__get____set__方法的类)。 If it does, resolve the attribute lookup by calling the __get__ method of the Data Descriptor (lines 28–33).如果是,则通过调用数据描述符的__get__方法(第 28-33 行)解析属性查找。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 实例属性和类属性之间的区别 - Difference between instance attributes and class attributes Python class方法之间如何共享变量,实例属性和class属性有什么区别 - Python how to share variable between class methods, and what is the difference between instance attributes and class attributes Python中的类属性,实例属性和实例方法之间的区别 - Difference between Class Attributes, Instance Attributes, and Instance Methods in Python 我了解Python中的实例属性和类属性之间的区别吗? - do I understand the difference between instance attributes and class attributes in Python? 类和数据属性之间有什么区别? - What is the difference between class and data attributes? 类变量和实例变量有什么区别? - What is the difference between class and instance variables? __init__() 内部和外部变量之间的区别(类和实例属性) - difference between variables inside and outside of __init__() (class and instance attributes) 类属性和实例属性之间混淆 - confused between class attributes and instance attributes Python类方法:实例成员和类成员之间的区别是什么? - Python classmethods: what's the difference between a member of an instance and a member of the class? 类和实例方法的区别 - Difference between Class and Instance methods
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM