Skip to main content
Engineering LibreTexts

19.7: defaultdict

  • Page ID
    42491
  • The collections module also provides defaultdict, which is like a dictionary except that if you access a key that doesn’t exist, it can generate a new value on the fly.

    When you create a defaultdict, you provide a function that’s used to create new values. A function used to create objects is sometimes called a factory. The built-in functions that create lists, sets, and other types can be used as factories:

    >>> from collections import defaultdict
    >>> d = defaultdict(list)
    

    Notice that the argument is list, which is a class object, not list(), which is a new list. The function you provide doesn’t get called unless you access a key that doesn’t exist.

    >>> t = d['new key']
    >>> t
    []
    

    The new list, which we’re calling t, is also added to the dictionary. So if we modify t, the change appears in d:

    >>> t.append('new value')
    >>> d
    defaultdict(<class 'list'>, {'new key': ['new value']})
    

    If you are making a dictionary of lists, you can often write simpler code using defaultdict. In my solution to Exercise 12.10.2, which you can get from http://thinkpython2.com/code/anagram_sets.py, I make a dictionary that maps from a sorted string of letters to the list of words that can be spelled with those letters. For example, ’opst’ maps to the list [’opts’, ’post’, ’pots’, ’spot’, ’stop’, ’tops’].

    Here’s the original code:

    def all_anagrams(filename):
        d = {}
        for line in open(filename):
            word = line.strip().lower()
            t = signature(word)
            if t not in d:
                d[t] = [word]
            else:
                d[t].append(word)
        return d
    

    This can be simplified using setdefault, which you might have used in Exercise 11.10.2:

    def all_anagrams(filename):
        d = {}
        for line in open(filename):
            word = line.strip().lower()
            t = signature(word)
            d.setdefault(t, []).append(word)
        return d
    

    This solution has the drawback that it makes a new list every time, regardless of whether it is needed. For lists, that’s no big deal, but if the factory function is complicated, it might be.

    We can avoid this problem and simplify the code using a defaultdict:

    def all_anagrams(filename):
        d = defaultdict(list)
        for line in open(filename):
            word = line.strip().lower()
            t = signature(word)
            d[t].append(word)
        return d
    

    My solution to Exercise 18.2.3, which you can download from http://thinkpython2.com/code/PokerHandSoln.py, uses setdefault in the function has_straightflush. This solution has the drawback of creating a Hand object every time through the loop, whether it is needed or not. As an exercise, rewrite it using a defaultdict.

    • Was this article helpful?