[英]Why the class attribute does not always appear as key in the instance's dictionary?
我在关于属性的这一部分的 python 中阅读了关于 OOP 的内容,并被下面的示例震惊了。
我不明白为什么实例的字典(其class
被赋予新属性)为空:
class Robot(object):
pass
x = Robot()
Robot.brand = "Kuka"
x.brand
Output:
'Kuka'
x.brand = "Thales"
Robot.brand
Output:
'Kuka'
y = Robot()
y.brand
Output:
'Kuka'
Robot.brand = "Thales"
y.brand
Output:
'Thales'
x.brand
Output:
'Thales'
如果您查看__dict__
字典,您可以看到发生了什么:
x.__dict__
Output:
{'brand': 'Thales'}
y.__dict__
Output:
{}
来自同一网站的另一句话:
如果您尝试访问 y.brand,Python 首先检查“brand”是否是 y 的键。
__dict__
字典。 如果不是,Python 检查“品牌”是否是机器人的键。__dict__
。 如果是这样,则可以检索该值。
我的问题是:为什么y.__dict__
给出一个空字典? 其背后的逻辑和机制是什么?
为什么会有一个? 实例不会自动复制 class 属性(这将是意外且低效的)。 您的y
object 没有任何实例属性( y.brand
只是Robot.brand
的代理,因为您在问题中发布了文档中的引用),因此它的.__dict__
是空的。
查看__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__)
使用以下 output:
{'i_var': 2}
{'i_var': 3}
如您所见, foo.__dict__
和bar.__dict__
的 output不包括class_var
。 所有类都是这种情况; __dict__
没有列出类范围的属性。
查看您的整个程序:
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__
我们可以看到,在创建x
和y
时, Robot.brand
被设置为"Kuka"
。 我们还看到有一个明确的声明将x.brand
设置为"Thales"
; 但是,它不包括y.brand = "Whatever"
声明。
换句话说, y.brand
的值是从 class 属性Robot.brand
继承的,而 class 属性没有与__dict__
一起列出。 另一方面, x.brand
专门设置为"Thales"
,因此x.brand
现在指的是特定于对象的变量,这意味着它将显示在__dict__
列表中。
当您尝试访问实例中的方法或属性时,python 首先询问该实例,如果该实例有这样的东西。 如果没有,那么 python 会询问父级是否有那个东西等。 例如,
class A:
x = 9
“x”是一个 class 变量,与 class 一起存在。 因此,所有实例都自动具有“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"
这是非常高效的 memory。 例如,这意味着所有实例共享一个“全局”(相对于类的全局)变量。 因此,在 memory 中只写入了一份 Ax。 唯一一次 python 在 memory 中写入另一个“x”值是在实例显式更改其“x”值的情况下。 因此,生成一百万个 A 实例并不会生成一百万个 Ax 值的副本
我们可以使用id(<object>)
来检查发生了什么。
新空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})
Robot
class 的新实例
x = Robot()
x
<__main__.Robot object at 0x0000022811C8A978>
x
实例为空
x.__dict__
{}
新class物业brand
定义
Robot.brand = "Kuka"
Robot.brand
'Kuka'
如果我们尝试访问x.brand
, Python 将在x.__dict__
中查找brand
,找不到任何东西,所以它会转到Robot.__dict__
并找到 class 属性brand
。
x.brand
'Kuka'
我们可以验证我们实际上看到的是相同的
id(Robot.brand)
2371120205752
id(x.brand)
2371120205752
定义了新的实例属性brand
x.brand = "Thales"
并且 class 物业brand
保持不变
Robot.brand
'Kuka'
我们可以验证我们实际上看到了两个不同的属性
id(x.brand)
2371119992200
id(Robot.brand)
2371120205752
新实例y
已创建并且为空
y = Robot()
y.__dict__
{}
我们可以验证这是一个新的:
id(y)
2371119989200
如果我们尝试访问y.brand
, Python 将在y.__dict__
中寻找brand
,没有找到任何东西然后去Robot.__dict__
并找到 class 属性brand
。
y.brand
'Kuka'
我们可以验证 id 是否与Robot.brand
相同。 所以y
是对Robot.brand
的引用
id(y.brand)
2371120205752
如果我们修改 class 属性brand
Robot.brand = "Thales"
id(Robot.brand)
2371119992200
y.brand
被修改,因为此时不是实例属性,而是对 class 属性Robot.brand
的引用。 我们可以检查Robot.brand
的 id 是否与y.brand
相同。
y.brand
'Thales'
id(y.brand)
2371119992200
现在我们可以检查x
是否有一个实例属性x.brand
x.brand
'Thales'
x.__dict__
{'brand': 'Thales'}
但是 y 什么都没有,因为它只是对Robot.brand
的引用
y.__dict__
{}
而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'})
在此处查看附加说明: https://github.com/leocjj/0123/blob/master/Python/0123P_9_Classes_objects.txt
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.