开发者

Dictionary and array as class vs. instance variables

This is an easy way to earn some points. Please explain the following:

class C:
    a = {}
    b = 0
    c = []

    def __init__(self):
        self.x = {}

    def d(self, k, v):
        self.x[k] = v
        self.a[k] = v;
        self.b = v
        self.c.append(v)

    def out(self, k):
        print(self.x[k], self.a[k], self.b, self.c[0])

c = C()
d = C()
c.d(1, 10)
d.d(1, 20)
c.out(1)  
d.out(1)

Wil开发者_运维技巧l output the following:

10 20 10 10
20 20 20 10

Why does a dictionary, a list and 'plain' variable each behave differently?

Edit: I was thinking the question is obvious but let me spell it in greater detail:

I have a class with three attributes, a, b, and c. I create two instances of the class. I then call a method that modifies these attributes for each instance. When I inspect the attributes, I find that if an attribute is a dictionary, it is shared across all instances, while if it is a 'plain' variable, it behaves as one would expect, being different for each instance.


First of all, [] is not an array, it's a list. The issue here is how the attribute resolution and mutable variables work. Let's start with

class Foo(object):
    a = {}
    b = 0
    c = []

This creates a class with three attributes — those are available either through class itself (Foo.a, for example), or through class' instance (Foo().a). Attributes are stored in a special thingy called __dict__. Both class and an instance have one (there are cases in which this is not true, but they're irrelevant here) — but in the case of Foo, the instance __dict__ is empty when the instance is created — so when you do Foo().a, in reality you're accessing the same object as in Foo.a.

Now, you're adding __init__.

class Foo(object):
    # ...

    def __init__(self):
        self.x = {}

This creates an attribute not in the class' __dict__, but in the instance one, so you cannot access Foo.x, only Foo().x. This also means x is a whole different object in every instance, whereas class attributes are shared by all of the class instances.

Now you're adding your mutation method.

class Foo(object):
    # ...

    def mutate(self, key, value):
        self.x[key] = value
        self.a[key] = value
        self.b      = value
        self.c.append(value)

Do you recall that self.x = {} creates an instance attribute? Here self.b = value does the same exact thing — it doesn't touch the class attribute at all, it creates a new one that for instances overshadows the shared one (that's how references work in Python — assignment binds the name to an object, and never modifies the object that the name was pointing to).

But you don't rebind self.a and self.c — you mutate them in-place (because they're mutable and you can do that) — so in fact you're modifying the original class attributes, that's why you can observe the change in the other instance (as those are shared by them). self.x behave differently, because it's not a class attribute, but rather an instance one.

You also print only first element of self.c — if you'd print all of it, you'd see it's [10, 20].


a and c are class attributes b and x are instance attributes.

You should read and understand Python: Difference between class and instance attributes

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜