64

I wish to have a dictionary which contains a set of state transitions. I presumed that I could do this using states = defaultdict(None), but it's not working as I expected. For example:

states = defaultdict(None)
if new_state_1 != states["State 1"]:
    dispatch_transition()

I would have thought that states["State 1"] would return the value None and that if new_state is a bool that I would have gotten False for new_state != states["State 1"], but instead I get a KeyError.

What am I doing wrongly?

4 Answers 4

132

defaultdict requires a callable as argument that provides the default-value when invoked without arguments. None is not callable. What you want is this:

defaultdict(lambda: None)
7
  • 12
    Not quite; None is actually the default argument for defaultdict(). But your solution is still good :) Commented Oct 25, 2011 at 8:09
  • 1
    [confused] What is the "not quite" here? did I miss an earlier edit that was not retained by the system? I don't see anything incorrect with Bjorn's explanation or answer as is.
    – Jason S
    Commented May 30, 2013 at 17:14
  • 3
    @JasonS: My answer suggests that calling deafultdict with None as argument would not work, but it actually does. Commented May 30, 2013 at 18:51
  • 17
    ??? Huh? I agree None is the default argument to defaultdict(), but its effect is to cause defaultdict to raise KeyErrors instead of producing a None value.
    – Jason S
    Commented May 31, 2013 at 1:16
  • 3
    @JasonS: True, but since None is a valid argument for defaultdict, my first sentence ("defaultdict requires a callable as argument") is, strictly speaking, incorrect. It's a detail, but still worth noting. Commented May 31, 2013 at 6:31
16

In this use case, don't use defaultdict at all -- a plain dict will do just fine:

states = {}
if new_state_1 != states.get("State 1"):
    dispatch_transition()

The dict.get() method returns the value for a given key, or a default value if the key is not found. The default value defaults to None.

1

A similar solution as the answer by Björn Pollex: Instead of using lambda: None to obtain a zero-argument callable that returns None, we can supply the type of None (similar to the idiom defaultdict(int) to construct a defaultdict which defaults to int() = 0.

The type of None is NoneType, obtainable from the types module:

import types
defaultdict(types.NoneType)

If importing a new module just for this seems a bit much, we can obtain the type of None directly by asking about it:

defaultdict(type(None))
0

I guess I could also do this:

states = {}
...
if not new_state_1 in states or new_state_1 != states["State 1"]:
    dispatch_transition()

But I much prefer the defaultdict method.

Not the answer you're looking for? Browse other questions tagged or ask your own question.