python 42 lines · 8 steps

Building a case-insensitive dict in Python

A dict that matches keys regardless of case while preserving the original casing you stored.

Explained by highlit
1from collections.abc import MutableMapping
2 
3 
4class CaseInsensitiveDict(MutableMapping):
5 def __init__(self, data=None, **kwargs):
6 self._store = {}
7 if data is not None:
8 self.update(data)
9 self.update(kwargs)
10 
11 def __setitem__(self, key, value):
12 self._store[key.lower()] = (key, value)
13 
14 def __getitem__(self, key):
15 return self._store[key.lower()][1]
16 
17 def __delitem__(self, key):
18 del self._store[key.lower()]
19 
20 def __iter__(self):
21 return (original for original, _ in self._store.values())
22 
23 def __len__(self):
24 return len(self._store)
25 
26 def __contains__(self, key):
27 return isinstance(key, str) and key.lower() in self._store
28 
29 def __eq__(self, other):
30 if isinstance(other, MutableMapping):
31 other = CaseInsensitiveDict(other)
32 else:
33 return NotImplemented
34 return {k.lower(): v for k, v in self.items()} == {
35 k.lower(): v for k, v in other.items()
36 }
37 
38 def copy(self):
39 return CaseInsensitiveDict(self._store.values())
40 
41 def __repr__(self):
42 return f"{type(self).__name__}({dict(self.items())!r})"
01 / 01
STEP 01

Walkthrough

Space play step click any line
Three takeaways
  1. 1Subclassing MutableMapping gives you a full dict-like interface from just five required methods.
  2. 2Storing a lowercased key alongside the original preserves display casing while enabling case-insensitive lookup.
  3. 3Overriding __eq__ to normalize both sides lets equality ignore casing consistently.

Related explainers

Share this explainer

Here's the card — post it anywhere.

Building a case-insensitive dict in Python — share card
Made with highlit — turn any snippet into a walkthrough like this in about a minute.
Explain your code