简体   繁体   English

为什么 class 属性并不总是作为键出现在实例的字典中?

[英]Why the class attribute does not always appear as key in the instance's dictionary?

I was reading about OOP in python on this section about Attributes and was literally shocked by the below example.我在关于属性的这一部分的 python 中阅读了关于 OOP 的内容,并被下面的示例震惊了。

I am not getting why the dictionary of an instance (whose class is given a new attribute) is empty:我不明白为什么实例的字典(其class被赋予新属性)为空:

class Robot(object):
    pass

x = Robot()
Robot.brand = "Kuka"
x.brand

Output: Output:

'Kuka'

x.brand = "Thales"
Robot.brand

Output: Output:

'Kuka'

y = Robot()
y.brand

Output: Output:

'Kuka'

Robot.brand = "Thales"
y.brand

Output: Output:

'Thales'

x.brand

Output: Output:

'Thales'

If you look at the __dict__ dictionaries, you can see what's happening:如果您查看__dict__字典,您可以看到发生了什么:

x.__dict__

Output: Output:

{'brand': 'Thales'}

y.__dict__

Output: Output:

{}

Another quote from same website:来自同一网站的另一句话:

If you try to access y.brand, Python checks first, if "brand" is a key of the y.如果您尝试访问 y.brand,Python 首先检查“brand”是否是 y 的键。 __dict__ dictionary. __dict__字典。 If it is not, Python checks if "brand" is a key of the Robot.如果不是,Python 检查“品牌”是否是机器人的键。 __dict__ . __dict__ If so, the value can be retrieved.如果是这样,则可以检索该值。

My question is: Why does y.__dict__ give an empty dictionary?我的问题是:为什么y.__dict__给出一个空字典? What is the logic and mechanism behind it?其背后的逻辑和机制是什么?

Why would it have one?为什么会有一个? Instances do not automatically copy class attributes (that would be unexpected and inefficient).实例不会自动复制 class 属性(这将是意外且低效的)。 Your y object doesn't have any instance attributes ( y.brand is simply a proxy to Robot.brand because of the quote from the documentation you posted in your question), hence its .__dict__ is empty.您的y object 没有任何实例属性( y.brand只是Robot.brand的代理,因为您在问题中发布了文档中的引用),因此它的.__dict__是空的。

Looking at this tutorial on __dict__ , we can see the following code sample (slightly modified from original):查看__dict__上的本教程,我们可以看到以下代码示例(对原始代码稍作修改):

class MyClass(object):
    class_var = 1

    def __init__(self, i_var):
        self.i_var = i_var

foo = MyClass(2)
bar = MyClass(3)

print(foo.__dict__)
print(bar.__dict__)

With the following output:使用以下 output:

{'i_var': 2}
{'i_var': 3}

As you can see, the output of foo.__dict__ and bar.__dict__ do not include class_var .如您所见, foo.__dict__bar.__dict__的 output包括class_var This is the case with all classes;所有类都是这种情况; __dict__ does not list class-wide attributes. __dict__没有列出类范围的属性。

Looking at your entire program:查看您的整个程序:

class Robot(object):
    pass
x = Robot()
Robot.brand = "Kuka"
x.brand
x.brand = "Thales"
Robot.brand
y = Robot()
y.brand
Robot.brand = "Thales" # Sets class variable
y.brand
x.brand
x.__dict__
y.__dict__

We can see that, at the time x and y were created, Robot.brand was set to "Kuka" .我们可以看到,在创建xy时, Robot.brand被设置为"Kuka" We also see that there was an explicit declaration setting x.brand to "Thales" ;我们还看到有一个明确的声明将x.brand设置为"Thales" however, it does not include a y.brand = "Whatever" declaration.但是,它不包括y.brand = "Whatever"声明。

In other words, the value of y.brand is inherited from the class attribute Robot.brand , and class attributes are not listed with __dict__ .换句话说, y.brand的值是从 class 属性Robot.brand继承的,而 class 属性没有与__dict__一起列出。 On the other hand, x.brand is specifically set to "Thales" , so x.brand is now referring to an object-specific variable, meaning it will show up in a __dict__ listing.另一方面, x.brand专门设置为"Thales" ,因此x.brand现在指的是特定于对象的变量,这意味着它将显示在__dict__列表中。

When you attempt to access a method or attribute in an instance, then python first asks the instance if the instance has such a thing.当您尝试访问实例中的方法或属性时,python 首先询问该实例,如果该实例有这样的东西。 If not, then python asks the parent if the parent has that thing, etc. Python continues climbing the layers of inheritance till it finds something or it raises and error.如果没有,那么 python 会询问父级是否有那个东西等。 For example,例如,

class A: 
    x = 9

"x" is a class variable and exists with the class. “x”是一个 class 变量,与 class 一起存在。 Hence, ALL instances have "x" attribute automatically, unless they override their own attribute.因此,所有实例都自动具有“x”属性,除非它们覆盖自己的属性。

I = A()
print(I.x) #prints 9, python asks the parent for "x" since I didn't have an "x"
I.x = 10    
print(I.x) #prints 10, python returns what I already has, doesn't ask parent

Z = A() 
print(Z.x) #prints 9, python asks the parent for "x" since Z didn't have an "x"

This is very memory efficient.这是非常高效的 memory。 For example, this means that all instances share a "global" (global with respect to the class) variable.例如,这意味着所有实例共享一个“全局”(相对于类的全局)变量。 Hence, only one copy of Ax is written in memory.因此,在 memory 中只写入了一份 Ax。 The only time python writes another "x" value in memory is if an instance changed their "x" value explicitly.唯一一次 python 在 memory 中写入另一个“x”值是在实例显式更改其“x”值的情况下。 So generating a million instances of A does not make a million copies of the value at Ax因此,生成一百万个 A 实例并不会生成一百万个 Ax 值的副本

We can use id(<object>) to check what's going on.我们可以使用id(<object>)来检查发生了什么。

Declaration of a new empty class Robot新空class机器人申报

    class Robot(object):
    ...     pass
    ... 
    Robot
    <class '__main__.Robot'>
    Robot.__dict__
    mappingproxy({'__module__': '__main__', '__dict__': <attribute '__dict__' of 'Robot' objects>, '__weakref__': <attribute '__weakref__' of 'Robot' objects>, '__doc__': None})

New instance of Robot class Robot class 的新实例

    x = Robot()
    x
    <__main__.Robot object at 0x0000022811C8A978>

The x instance is empty x实例为空

    x.__dict__
    {}

New class property brand is defined新class物业brand定义

Robot.brand = "Kuka"
Robot.brand
'Kuka'

If we try to access x.brand , Python will look for brand in x.__dict__ , find nothing so it goes to Robot.__dict__ and find the class property brand .如果我们尝试访问x.brand , Python 将在x.__dict__中查找brand ,找不到任何东西,所以它会转到Robot.__dict__并找到 class 属性brand

x.brand
'Kuka'

We can verify that we are actually seen the same我们可以验证我们实际上看到的是相同的

id(Robot.brand)
2371120205752
id(x.brand)
2371120205752

New instance property brand is defined定义了新的实例属性brand

x.brand = "Thales"

And the class property brand remains unaltered并且 class 物业brand保持不变

Robot.brand
'Kuka'

We can verify that we are actually seen two different properties我们可以验证我们实际上看到了两个不同的属性

id(x.brand)
2371119992200
id(Robot.brand)
2371120205752

New instance y is created and is empty新实例y已创建并且为空

y = Robot()
y.__dict__
{}

We can verify that is a new one:我们可以验证这是一个新的:

id(y)
2371119989200

If we try to access y.brand , Python will look for brand in y.__dict__ , found nothing so then goes to Robot.__dict__ and find the class property brand .如果我们尝试访问y.brand , Python 将在y.__dict__中寻找brand ,没有找到任何东西然后去Robot.__dict__并找到 class 属性brand

y.brand
'Kuka'

And we can verify that the id is the same of Robot.brand .我们可以验证 id 是否与Robot.brand相同。 So y is a reference to Robot.brand所以y是对Robot.brand的引用

id(y.brand)
2371120205752

If we modify class property brand如果我们修改 class 属性brand

Robot.brand = "Thales"
id(Robot.brand)
2371119992200

y.brand is modified because at this moment isn't an instance property but a reference to class property Robot.brand . y.brand被修改,因为此时不是实例属性,而是对 class 属性Robot.brand的引用。 We can check the id of y.brand is the same as Robot.brand .我们可以检查Robot.brand的 id 是否与y.brand相同。

y.brand
'Thales'
id(y.brand)
2371119992200

Now we can check that x has an instance property x.brand现在我们可以检查x是否有一个实例属性x.brand

x.brand
'Thales'
x.__dict__
{'brand': 'Thales'}

but y has nothing because is just a reference to Robot.brand但是 y 什么都没有,因为它只是对Robot.brand的引用

y.__dict__
{}

and Robot.brand has the class property brand with value Thales而Robot.brand拥有价值Thales的class自有brand

Robot.__dict__
mappingproxy({'__module__': '__main__', '__dict__': <attribute '__dict__' of 'Robot' objects>, '__weakref__': <attribute '__weakref__' of 'Robot' objects>, '__doc__': None, 'brand': 'Thales'})

See additional notes here: https://github.com/leocjj/0123/blob/master/Python/0123P_9_Classes_objects.txt在此处查看附加说明: https://github.com/leocjj/0123/blob/master/Python/0123P_9_Classes_objects.txt

暂无
暂无

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

相关问题 将字典关键字设置为类实例的属性 - Set dictionary key to attribute of class instance Python:将属性添加到实例,使其出现在类中 - Python: add attribute to instance, have it appear in class 为什么python类属性的语义在赋值给实例后会发生变化? - Why does the semantics of a python class attribute change after assignment to an instance? 为什么这在 Python 中有效? 创建一个实例,在该类中设置一个不存在的属性,然后通过该实例打印该属性 - Why does this work in Python? Creating a instance, setting a non existent attribute in that class, then printing that attribute through the instance 为什么这本由 Sympy 返回的字典似乎有也没有键“w”? - Why does this dictionary, returned by Sympy, appear to have and also not have the key 'w'? 从 Python 字典中打印 class 实例属性 - Print class instance attribute from Python dictionary Python:键类型在字典中应该是不可变的,但为什么我可以让节点类的实例成为键? - Python: key type should be immutable in dictionary, but why I can let an instance of node class be a key? 为什么可以将一个类传递给 Python 的 sorted() 函数的 key 属性? - Why is it OK to pass a class to the key attribute of Python's sorted() function? 调用is_instance()似乎无法实例化类 - Calling is_instance() does not appear to instantiate class 为什么实例没有属性? - why does the instance have no attribute?
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM