Reputation: 614
I have read many of the questions posted on variable referencing in Python, but things are still happening as I play with code that I would not expect and its getting quite frustrating. Take this piece of code:
class MyClass(object) :
class_var = "original message" # same for all objects
def __init__(self, instance_var) :
self.instance_var = instance_var #particular to the object
my_object = MyClass("my instance variable")
untouched_object = MyClass("another instance_var")
# Values of class_var and instance_var as expected
MyClass.class_var = "changed message"
# attribute class_var has changed for both objects
# This changes attribute class_var for my_object only
my_object.class_var = "I am my_object"
# THIS ONLY CHANGES ATTRIBUTE CLASS_VAR FOR UNTOUCHED_OBJECT ONLY!
MyClass.class_var = "Last message"
print MyClass.class_var #prints Last Message
print my_object.class_var #prints I am my_object ???
print untouched_object.class_var #prints Last Message
What is going on here? When I create an object of some class with a class variable, it seems that object.class_attribute
originally contains a pointer to Class.class_var
and I can change the attribute for all objects by assigning to Class.class_var
. But if I explicitly assign object.class
attribute to some other value, the pointer disappears and altering Class.class_var
no longer has an effect on that particular object changing, only all other instances. Is this whats going on here? Further, why would we be able to access and change a class attribute in this way and potentially lose access to a value that the Python Documentation purports is known to all instances?
Upvotes: 1
Views: 1019
Reputation: 11009
Every object has a variable named __dict__
that stores the values of its member variables, indexed by name. Every class also has a variable named __dict__
that stores the values of its class variables, indexed by name. When you access a variable v of an instance a with the a.v syntax, Python first looks for a.__dict__['v']
. If there is no such element, it looks in the class dictionary, i.e., a.__class__.__dict__['v']
. That's how class variables become visible to each instance of that class.
But when you assign to a variable the process is different. A statement like a.v = 12
causes Python to perform a.__dict__['v'] = 12
. It doesn't touch the class dictionary. So other instances of the class are unaffected, as you would expect. Now when you access a.v you get the value 12, the instance-specific value you just set, rather than the value of the class variable. And you can no longer change a.v by messing with the class variable since a
now has its own variable named 'v'.
The upshot of this is that you should use class variables mostly for setting "constants," similar to the way you use an enum or a #define in C. It's a value that you don't intend to change. If you restrict class variables to that style of usage, they are quite handy and easy to understand.
Upvotes: 2
Reputation: 55499
When you do
my_object.class_var = "I am my_object"
you create an instance attribute my_object.class_var
that shadows the class attribute MyClass.class_var
, so it appears that the subsequent assignment to MyClass.class_var
has no effect on my_object
.
It can be misleading to try and understand Python's data model in terms of concepts from other languages, eg references and pointers. Try to think in terms of names, and the binding of objects to names.
So in your example, MyClass.class_var
and my_object.class_var
are originally both names bound to the class attribute, but the assignment binds a new string object to the name my_object.class_var
.
I find that it helps to think of (name, object) pairs as (key, value) pairs in a dictionary. In some cases that's actually what's happening "under the hood" (eg with normal classes you create yourself), but in other cases (like Python's built-in types) things are implemented a little differently for reasons of efficiency.
You may find this article helpful: Facts and myths about Python names and values, which was written by SO veteran Ned Batchelder.
Upvotes: 3