Python
dict override
coding tips
Python dictionary
programming tricks

How to perfectly override a dict?

Master System Design with Codemia

Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.

Introduction

If you want custom dictionary behavior in Python, subclassing dict is not always the best answer. The important detail is that some built-in dict operations bypass your overridden methods, so the "perfect" override is usually built on collections.UserDict or collections.abc.MutableMapping instead.

Why subclassing dict can surprise you

A raw dict subclass looks straightforward:

python
1class MyDict(dict):
2    def __setitem__(self, key, value):
3        print("setting", key)
4        super().__setitem__(key, value)

That works for direct assignment like d["x"] = 1. But some inherited operations may manipulate the internal dictionary state in ways that do not go through every override you expect.

That is why developers often run into confusing behavior with methods such as initialization, update, or copying when trying to enforce validation or normalization rules.

Prefer UserDict for custom behavior

collections.UserDict is a wrapper around an internal real dictionary. Because it is implemented in Python, your overrides are applied more consistently.

Here is a useful example that normalizes all string keys to lowercase:

python
1from collections import UserDict
2
3
4class CaseInsensitiveDict(UserDict):
5    def __setitem__(self, key, value):
6        if isinstance(key, str):
7            key = key.lower()
8        super().__setitem__(key, value)
9
10    def __getitem__(self, key):
11        if isinstance(key, str):
12            key = key.lower()
13        return super().__getitem__(key)
14
15    def __contains__(self, key):
16        if isinstance(key, str):
17            key = key.lower()
18        return super().__contains__(key)
19
20
21settings = CaseInsensitiveDict()
22settings["Host"] = "localhost"
23print(settings["host"])
24print("HOST" in settings)

This gives you clear, predictable customization without fighting the C-level internals of dict.

When MutableMapping is the better abstraction

If you want full control over storage, validation, or computed values, implementing collections.abc.MutableMapping can be even cleaner. You provide the core mapping methods and let the mixin build the rest.

python
1from collections.abc import MutableMapping
2
3
4class LoggedDict(MutableMapping):
5    def __init__(self):
6        self._store = {}
7
8    def __getitem__(self, key):
9        return self._store[key]
10
11    def __setitem__(self, key, value):
12        print(f"set {key}={value}")
13        self._store[key] = value
14
15    def __delitem__(self, key):
16        del self._store[key]
17
18    def __iter__(self):
19        return iter(self._store)
20
21    def __len__(self):
22        return len(self._store)

This approach is ideal when your object is logically dictionary-like but should not behave exactly like a built-in dict.

What "perfectly override" usually means in practice

Most questions about overriding dictionaries are really about one of these goals:

  • validating keys or values
  • transforming keys before storage
  • logging reads and writes
  • providing defaults on missing keys
  • exposing a dictionary-like API over custom storage

For those tasks, UserDict is usually the best default answer. It preserves the mapping interface while making override behavior much easier to reason about.

When raw dict subclassing is still acceptable

Subclassing dict directly is fine if your customization is small and you fully understand which operations your code uses. It can also be slightly faster than wrapper-based approaches.

But if correctness matters more than squeezing out a little extra performance, wrapper or abstract-base-class solutions are usually safer.

Common Pitfalls

The biggest mistake is assuming that overriding one or two methods on dict changes every dictionary operation. It does not always work that way.

Another issue is forgetting to normalize keys consistently across __setitem__, __getitem__, get, and __contains__. If only one of those methods applies the rule, your custom mapping behaves inconsistently.

It is also easy to reach for inheritance when composition would be simpler. If you only need a normal dictionary plus a helper function, a custom class may be unnecessary.

Finally, avoid calling a solution "perfect" unless you know how the inherited methods behave. In Python, the safest answer is usually not "subclass dict harder" but "use the right mapping base type."

Summary

  • Directly subclassing dict is not always reliable for deep customization.
  • 'collections.UserDict is usually the best choice for consistent override behavior.'
  • 'MutableMapping is ideal when you want full control over storage and semantics.'
  • Keep key normalization and validation rules consistent across all access paths.
  • Choose the simplest abstraction that satisfies the behavior you actually need.

Course illustration
Course illustration

All Rights Reserved.