Welcome, guest | Sign In | My Account | Store | Cart

A pure-Python version of Python 2.5's defaultdict

Python, 37 lines
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
try:
    from collections import defaultdict
except:
    class defaultdict(dict):
        def __init__(self, default_factory=None, *a, **kw):
            if (default_factory is not None and
                not hasattr(default_factory, '__call__')):
                raise TypeError('first argument must be callable')
            dict.__init__(self, *a, **kw)
            self.default_factory = default_factory
        def __getitem__(self, key):
            try:
                return dict.__getitem__(self, key)
            except KeyError:
                return self.__missing__(key)
        def __missing__(self, key):
            if self.default_factory is None:
                raise KeyError(key)
            self[key] = value = self.default_factory()
            return value
        def __reduce__(self):
            if self.default_factory is None:
                args = tuple()
            else:
                args = self.default_factory,
            return type(self), args, None, None, self.items()
        def copy(self):
            return self.__copy__()
        def __copy__(self):
            return type(self)(self.default_factory, self)
        def __deepcopy__(self, memo):
            import copy
            return type(self)(self.default_factory,
                              copy.deepcopy(self.items()))
        def __repr__(self):
            return 'defaultdict(%s, %s)' % (self.default_factory,
                                            dict.__repr__(self))

collections.defaultdict is a handy shortcut added in Python 2.5 which can be emulated in older versions of Python. This recipe tries to backport defaultdict exactly and aims to be safe to subclass and extend without worrying if the base class is in C or is being emulated.

2 comments

Aaron Lav 14 years, 7 months ago  # | flag

problems pickling. This caused a segmentation fault when pickling with cPickle for me under Python 2.4.4. The __reduce__ method is documented at http://docs.python.org/lib/node321.html as requiring "an iterator (not a sequence)" for the 5th element of the tuple. Once I changed that items() to iteritems(), it worked perfectly. Thanks!

Jack Trainor 12 years, 10 months ago  # | flag

This is a better explanation of how defaultdict works than all the documentation and web discussion I've seen.