pyguides

__missing__

__missing__(self, key)

__missing__ is a dict subtype hook that Python calls automatically when a key lookup fails. It lets you define custom behaviour for d[key] access instead of the default KeyError.

When __missing__ is called

Python calls __missing__ only after dict.__getitem__ has already raised KeyError. This means the hook only applies to classes that inherit directly from dict — not from collections.abc.Mapping.

The lookup chain for d[key]:

dict.__getitem__(d, key)
  → key found? return value
  → key NOT found? raise KeyError
    → Python catches KeyError, calls d.__missing__(key)
__missing__ returns a value? that becomes the result of d[key]
__missing__ raises KeyError? it propagates

If __getitem__ returns NotImplemented (for cooperative multiple inheritance), Python does not fall back to __missing__.

Basic example

class StrDict(dict):
    def __missing__(self, key):
        return f"[missing: {key}]"

d = StrDict({"name": "Alice"})
print(d["name"])  # Alice
print(d["age"])   # [missing: age]

The return value of __missing__ becomes the result of the original d[key] expression.

Auto-vivification with nested dicts

A common use case is building nested dicts on demand — the “autovivification” pattern:

class TreeDict(dict):
    def __missing__(self, key):
        self[key] = TreeDict()
        return self[key]

tree = TreeDict()
tree["a"]["b"]["c"] = 42
print(tree["a"]["b"]["c"])  # 42

Without __missing__, accessing tree["a"] on an empty TreeDict would raise KeyError. With it, the nested dict is created and returned silently.

Relationship to collections.defaultdict

collections.defaultdict is implemented using __missing__ internally:

class defaultdict_(dict):
    def __init__(self, factory, **kwargs):
        super().__init__(**kwargs)
        self.default_factory = factory

    def __missing__(self, key):
        self[key] = self.default_factory()
        return self[key]

The key difference: defaultdict calls a factory callable every time a missing key is accessed. With __missing__ you have full control — you can return a static value, compute something, mutate state, or raise an error.

What does NOT trigger __missing__

  • dict.get(key) — returns None or a default without calling __missing__
  • key in d — uses __contains__, not __getitem__
  • Subclasses of collections.abc.Mapping — they use their own __getitem__ protocol

Return value and error handling

__missing__ can return any object, including None. If you want the original KeyError to propagate instead of silently returning a default, raise it explicitly:

class StrictDict(dict):
    def __missing__(self, key):
        raise KeyError(key)

This is useful when you want to log or audit missing key attempts but still treat them as errors.

See Also

  • __setitem__ — controls what happens when you assign d[key] = value
  • __getattr__ — general attribute access hook
  • __hash__ — required for dict keys, often paired with __eq__