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.
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 withThis 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 ofFoo, the instance__dict__is empty when the instance is created — so when you doFoo().a, in reality you’re accessing the same object as inFoo.a.Now, you’re adding
__init__.This creates an attribute not in the class’
__dict__, but in the instance one, so you cannot accessFoo.x, onlyFoo().x. This also meansxis 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.
Do you recall that
self.x = {}creates an instance attribute? Hereself.b = valuedoes 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.aandself.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.xbehave 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].