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
精彩评论