4

I'm wondering why a dictionary, that is defined in a base class and is accessed from derived classes, is obviously present only in one memory location. A short example:

class BaseClass:
    _testdict = dict()
    _testint = 0

    def add_dict_entry(self):
        self._testdict["first"] = 1

    def increment(self):
        self._testint += 1

class Class1(BaseClass):
    pass

class Class2(BaseClass):
    pass

object1 = Class1()
object2 = Class2()

object1.add_dict_entry()
object1.increment()
print(object2._testdict)
print(object2._testint)

and the output is:

{'first': 1}
0

Why does a call to the "add_dict_entry" of object1 affect the dictionary of object2? Using integers ("increment") the base class variable is not affected.

Thanks a lot.

Lorenz

Mark Hildreth
  • 42,023
  • 11
  • 120
  • 109
Lorenz Winkler
  • 155
  • 1
  • 4
  • 1
    Related: ["Least Astonishment" in Python: The Mutable Default Argument](http://stackoverflow.com/q/1132941) – Martijn Pieters Aug 24 '12 at 15:05
  • possible duplicate of [Dictionary and array as class vs. instance variables](http://stackoverflow.com/questions/7277094/dictionary-and-array-as-class-vs-instance-variables) – David Robinson Aug 24 '12 at 15:05

2 Answers2

9

It's because _testdict is a class variable: it's defined only once, when the class is initially constructed. If you want it to be separate for each instance, make it an instance variable:

class BaseClass:
    _testint = 0

    def __init__(self):
        self._testdict = dict()

    def add_dict_entry(self):
        self._testdict["first"] = 1

(Note that you'd need to create __init__ methods for Class1 and Class2 as well, both of which would have to call BaseClass.__init__(self)).

_testint behaves differently because you're performing a rebinding operation on it rather than a mutating operation. ints are immutable, so you can't "change" one- self._testint += 1 is just syntactic sugar for self._testint = self._testint + 1. Similarly, you can perform a rebinding operation on self._testdict that won't be shared between instances- for example, self._testdict = {} will reset only that instance's _testdict.

David Robinson
  • 77,383
  • 16
  • 167
  • 187
  • 1
    +1; the only thing left to explain to the OP is why `_testint` and `_testdict` behave differently. – DSM Aug 24 '12 at 14:59
  • @DSM: Also just found a possible [duplicate](http://stackoverflow.com/questions/7277094/dictionary-and-array-as-class-vs-instance-variables) whose answer explains it pretty thoroughly. – David Robinson Aug 24 '12 at 15:06
2

In python, int is immutable, therefore the += operation will rebound the class variable into an instance variables. On the other hand, a dictionary indexing mutates the dictionary in place. A more comparable example would be

def add_dict_entry(self):
    # create a new dict
    tmp = dict(self._testdict)
    tmp["first"] = 1

    # shadow the class variable with an instance variables
    self._testdict = tmp
Lie Ryan
  • 62,238
  • 13
  • 100
  • 144