Search code examples
pythondictionarysuper

How can I update an attribute created by a base class' mutable default argument, without modifying that argument?


I've found a strange issue with subclassing and dictionary updates in new-style classes:

Python 2.6.2 (r262:71605, Apr 14 2009, 22:40:02) [MSC v.1500 32 bit (Intel)] on
win32
>>> class a(object):
...     def __init__(self, props={}):
...             self.props = props
...
>>> class b(a):
...     def __init__(self, val = None):
...             super(b, self).__init__()
...             self.props.update({'arg': val})
...
>>> class c(b):
...     def __init__(self, val):
...             super(c, self).__init__(val)
...
>>> b_inst = b(2)
>>> b_inst.props
{'arg': 2}
>>> c_inst = c(3)
>>> c_inst.props
{'arg': 3}
>>> b_inst.props
{'arg': 3}
>>>

In debug, in second call (c(3)) you can see that within a constructor self.props is already equal to {'arg': 2}, and when b constructor is called after that, it becomes {'arg': 3} for both objects!

also, the order of constructors calling is:

  a, b    # for b(2)
  c, a, b # for c(3)

If you replace self.props.update() with self.props = {'arg': val} in b constructor, everything will be OK, and will act as expected

But I really need to update this property, not to replace it.


Solution

  • props should not have a default value like that. Do this instead:

    class a(object):
        def __init__(self, props=None):
            if props is None:
                props = {}
            self.props = props
    

    This is a common python "gotcha".