繁体   English   中英

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

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

我们可以看到,在创建xy时, 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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM