简体   繁体   English

Python: __dict__ shows class attribute for the object of the class even when the behavior inside the __init__ mutates the class attribute

[英]Python: __dict__ shows class attribute for the object of the class even when the behavior inside the __init__ mutates the class attribute

From my understanding,据我了解,

For a mutable object like a list, += (augmented assignment) leads to mutation of the object if the underlying object supports mutation like list.对于像列表这样的可变 object,如果底层 object 支持类似列表的突变,+=(增强分配)会导致 object 的突变。 Of course, this does not work for immutable objects which do not support mutation as __iadd__ is not present and __add__ is the one that gets called.当然,这不适用于不支持突变的不可变对象,因为__iadd__不存在,而__add__是被调用的对象。

Consider the following code:考虑以下代码:

class TestClass:
    bar = []

    def __init__(self, x):
        self.bar += [x]


f = TestClass(1)
g = TestClass(2)
h = TestClass(3)
print(f.bar)
print(g.bar)
print(TestClass.bar)

Now, I understand why all three g,f,h print out [1, 2, 3] since we are "mutating" the class attribute.现在,我明白为什么所有三个 g,f,h 都会打印出[1, 2, 3]因为我们正在“改变” class 属性。

But when I try to look at the namespace for the objects by doing print(f.__dict__) , I see the bar attribute inside the object as well.但是当我尝试通过print(f.__dict__)查看对象的命名空间时,我也看到了 object 中的 bar 属性。

Question 1: If the actual attribute that gets mutated is the class attribute, why does the instance get created in object namespace?问题 1:如果实际发生突变的属性是 class 属性,为什么会在 object 命名空间中创建实例?

Secondly, "if" that instance variable gets created in the object(like f), then f.bar += 4 should only affect object f as the new instance variable bar in f supposedly shadowed the class attribute.其次,“如果”该实例变量在对象中创建(如 f),那么f.bar += 4应该只影响 object f,因为 f 中的新实例变量 bar 应该遮蔽了 class 属性。 But I see that doing f.bar+=4 in turn also updates the class attribute which is totally confusing to me.但我看到依次执行f.bar+=4也会更新 class 属性,这让我完全困惑。

Can someone please help me with this?有人可以帮我吗?

This may be a little subtle.这可能有点微妙。 You can think of你可以想到

self.bar += [x]

As equivalent to相当于

self.bar = self.bar.__iadd__([x])

So, self.bar.__iadd__([x]) mutates the list, essentially calling list.extend , it then returns the list object .因此, self.bar.__iadd__([x])改变列表,本质上调用list.extend ,然后返回列表 object This list object, which is the same one being referenced by TestClass.bar , is assigned to the instance attribute .此列表 object 与TestClass.bar引用的相同,已分配给实例属性

So now you have an instance attribute with the same name as the class attribute and both happen to be bound to the same list object所以现在你有一个与 class 属性同名的实例属性,并且两者都绑定到同一个列表 object

This is even warned for in the [documentation]:这甚至在 [documentation] 中被警告:

For targets which are attribute references, the same caveat about class and instance attributes applies as for regular assignments.对于属性引用的目标, 关于 class 和实例属性的相同警告适用于常规分配。

With that caveat being:需要注意的是:

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

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