[英]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"
.我们可以看到,在创建
x
和y
时, 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.