0

I am trying to write a wrapper object around the dictionary object in python like so

class ScoredList():
    def __init__(self,dct={}):
        self.dct = dct

list = ScoredList()
list.dct.update({1,2})

list2 = ScoredList()
list.dct.update({"hello","world"})

print list1.dct, list2.dct # they are the same but should not be!

It seems like I am unable to create a new ScoredList object, or rather, every scored list object shares the same underlying dictionary. Why is this?

class ScoredList2():
    def __init__(self):
        self.dct = {}

The above code for ScoredList2 works fine. But I want know how to overload the constructor properly in python.

kumikoda
  • 644
  • 6
  • 27
  • 4
    Obligatory link: http://stackoverflow.com/questions/1132941/least-astonishment-in-python-the-mutable-default-argument. – DSM May 21 '12 at 23:30
  • 3
    `{"hello","world"}` is not a dictionary, `{1,2}` is not either. – Tadeck May 21 '12 at 23:48

1 Answers1

4

A dictionary is a mutable object. In Python, default values are parsed when the function is created, meaning the same empty dictionary is assigned to every new object.

To solve this, simply do something like:

class ScoredList():
    def __init__(self, dct=None):
        self.dct = dct if dct is not None else {}
Gareth Latty
  • 86,389
  • 17
  • 178
  • 183
  • 4
    That should probably be `dct if dct is not None else {}`, otherwise if you pass an empty dictionary `__init__()` would create a new one instead of using the one you provided. – Andrew Clark May 21 '12 at 23:38
  • You could also do `dct or {}`. Hmmm maybe not as clear though. – Joel Cornett May 21 '12 at 23:50
  • @F.J I would argue that being a problem is a pretty rare case - but yes, if that would be a problem, your replacement would be best. Edited. – Gareth Latty May 22 '12 at 00:29
  • 1
    @JoelCornett I would argue that is much less clear. Short, yes, but that should never be the priority. – Gareth Latty May 22 '12 at 00:30
  • 1
    For the instances where you might want `None` to be a valid argument, use a sentinel. Stick `NO_ARGUMENT = object()` somewhere before your class def, then `def __init__(self, dct=NO_ARGUMENT):` etc – Matthew Trevor May 22 '12 at 01:00
  • @MatthewTrevor another way is `def __init__(self, dct = object()):`, and then check if `dct is ScoredList.func_defaults[0]`. – Karl Knechtel May 22 '12 at 02:00