Friday, October 17, 2014

Object references in class definitions

It's been a while. Things happen, I guess.
Today I learned a nice thing about Python. I don't know the specifics, but I was told that this is an intentional design decision in Python.
The thing is: When you declare an object in a class, the class uses a 'pointer' towards that object. (I'm using the word 'pointer' here. The specifics might be a bit different. Be free to elaborate in the comments). This means that if you have two instances of your class and you change one of the object variables, it is also changed in the second instance of your class.
Code example to make things clear:
# Declare a class with two variables
class TestObj(object):
    obj_var = {}  # object
    val_var = 11  # value

# Create two instances of the class
test_one = TestObj()
test_two = TestObj()

# Change both variables of one instance
test_one.obj_var['key'] = 'value'
test_one.val_var = 10

# Print both variables of both instances
Out: 10  # Expected
Out: 11  # Expected

Out: {'key': 'value'}  # Expected
Out: {'key': 'value'}  # Huh?

The solution?
class TestObj(object):
    val_var = 11

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

Instead of using __init__ you can of course declare the variable whenever you need it.

You might think it's a bad idea to change class variables on an instance of a class. You are correct, it feels bad and you probably shouldn't be doing things like I did in my example. However, look at the issue still exists when you let the class itself change its variables:
class TestObj(object):
    obj_var = {}
    def change_obj_var(self):
        self.obj_var['hello'] = 'world'
test_one = TestObj()
test_two = TestObj()


Out: {'hello': 'world'}  # Expected
Out: {'hello': 'world'}  # Huh?