defaultdict.fromkeys does not play nice.

I use defaultdicts a lot when I’m grouping elements into a dictionary of lists. Here’s a simple example:

>>> a = defaultdict(list)

>>> a['x']
[]

>>> a['y'].append('yellow')

>>> a
defaultdict(, {'y': ['yellow'], 'x': []})

Now here’s where I got silly. I used defaultdict.fromkeys to prepopulate the ‘x’ and ‘y’ key right away, because I know I needed those:

>>> b = defaultdict.fromkeys(['x', 'y'], list)

>>> b
defaultdict(None, {'y': , 'x': })

>>> b['x']

>>> b['z']
------------------------------------------------------------
Traceback (most recent call last):
File "", line 1, in
KeyError: 'z'

Wowsa! b calls itself a defaultdict, but it is not a defaultdict.

I haven’t really thought this through, but this behavior is so unexpected that I would prefer that defaultdict.fromkeys raised a NotImplementedError.

Published by

matt

My name is Matt Wilson and I live in Cleveland Heights, Ohio. I love random emails from strangers, so get in touch! matt@tplus1.com.

  • It looks like defaultdict.fromkeys just fails to pass the factory function along:

    >>> defaultdict(list)
    defaultdict(<type 'list'>, {})
    >>> defaultdict.fromkeys([], list)
    defaultdict(None, {})

    There's no mention of fromkeys in the collections module's source, so it's probably just inheriting it from dict. That makes sense – dict doesn't know about the default_factory. You can manually set the defaultdict's default_factory argument to fix it.

  • Hey Gary, thanks for the comment.

    Setting the default_factory after I make an instance doesn't go back and rerun the keys through the default factory. So, in that sense, fromkeys is kinda useless to me. That's why I'd prefer that it just raised that NotImplementedError.

    >>> b = defaultdict.fromkeys(['x', 'y'], list)
    >>> b
    defaultdict(None, {'y': <type 'list'>, 'x': <type 'list'>})
    >>> b.default_factory = list
    >>> b['z']
    []
    >>> b['x']
    <type 'list'>

  • Ahh… good point. Clearly I didn't think it through. 🙂

    I considered taking a stab at fixing this, but the effort involved for a first-time Python contributor is prohibitive. So laziness wins again.

  • Georg Brandl

    With dictionaries, the second argument for fromkeys() is the value (not a value factory) that all keys from the first argument will be set to. Since defaultdict.fromkeys() has no special handling, this works the same with defaultdict. That's not a bug to me.

    Of course defaultdict.fromkeys could be extended to take a factory function argument, but as you have seen you can set the factory yourself too.

    All in all, I don't see the value of using fromkeys() for a defaultdict.

  • Hey Georg,

    Yeah, I agree that the defaultdict.fromkeys behavior is not a bug, but it does seem a little surprising. Anyhow, I posted to comp.lang.python here about this topic and got some pretty good explanation.

  • Hey Georg,

    Yeah, I agree that the defaultdict.fromkeys behavior is not a bug, but it does seem a little surprising. Anyhow, I posted to comp.lang.python here about this topic and got some pretty good explanation.