0

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)

Will 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.

1
  • "Why does a dictionary, an array and 'plain' variable each behave differently?" They behave differently because they're different classes. That question doesn't make any sense at all. What specific difference do you want to know about? Commented Sep 1, 2011 at 22:24

2 Answers 2

5

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].

Sign up to request clarification or add additional context in comments.

2 Comments

If I understand you correctly, what it boils down to is: self.b = value creates new entry in instance.__dict__ while self.a[key] = value doesn't. I think that is the answer to my question: the instance dictionary is not populated when the instance is created, but only when the attribute is assigned to. Is this correct?
@martin: Yup. The default getattr first checks the instance dict, and then class dict if attribute wasn't found (+ bases and stuff, but basically this).
2

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

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

1 Comment

b is also a class attribute to start out and is getting replaced by an instance attribute.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.