How to Build a Two-Way Dictionary in Python

Implement a BiDict class that supports both forward key-to-value and reverse value-to-key lookups with a simple add, delete, and update API.

Medium Python 3.9+ Aug 9, 2026 Dictionaries & sets 11 views 0 copies

Python code

40 lines
Python 3.9+
class BiDict:
    def __init__(self, data=None):
        self.forward = {}
        self.backward = {}
        if data:
            self.update(data)

    def update(self, data):
        for key, value in data.items():
            self[key] = value

    def __setitem__(self, key, value):
        self.forward[key] = value
        self.backward[value] = key

    def __getitem__(self, key):
        return self.forward[key]

    def get_key(self, value):
        return self.backward[value]

    def __delitem__(self, key):
        value = self.forward.pop(key)
        del self.backward[value]

    def __len__(self):
        return len(self.forward)

    def __repr__(self):
        return f"BiDict({self.forward!r})"

if __name__ == "__main__":
    bd = BiDict({"apple": "fruit", "carrot": "vegetable"})
    print("Forward:", bd["apple"])
    print("Backward:", bd.get_key("vegetable"))
    bd["banana"] = "fruit"
    print("After add:", bd)
    del bd["carrot"]
    print("After delete:", bd)
    print("Size:", len(bd))

Output

stdout
Forward: fruit
Backward: vegetable
After add: BiDict({'apple': 'fruit', 'carrot': 'vegetable', 'banana': 'fruit'})
After delete: BiDict({'apple': 'fruit', 'banana': 'fruit'})
Size: 2

How it works

The BiDict class keeps two parallel dictionaries: forward maps keys to values and backward maps values back to keys. Every insertion via __setitem__ writes to both maps, keeping them in sync. Deleting a key removes the entry from both dicts using pop and the mirrored value. Because both lookups are plain dictionary accesses, forward and reverse lookups are O(1) on average. The class exposes standard dict-like methods (__getitem__, __delitem__, __len__) so it behaves naturally in loops and expressions.

Common mistakes

  • Forgetting to enforce unique values — if two keys map to the same value, the backward map silently drops the first key.
  • Mutating the `.forward` dictionary directly and leaving `.backward` out of sync.
  • Not handling missing keys on reverse lookup with `.get()` or `KeyError`.
  • Assuming values are hashable — unhashable values like lists will crash in the backward dict.

Variations

  1. Use the third-party `bidict` package for a production-ready, battle-tested implementation.
  2. Build a dataclass wrapper around two dicts and add type hints for compile-time safety.

Real-world use cases

  • Mapping user IDs to usernames in a session manager so you can look up either direction fast.
  • Translating between internal enum names and API response codes in a service layer.
  • Matching city names to airport codes in a route-planning microservice without scanning lists.

Sponsored

Run this sample

Open the browser IDE to tweak the example and see results without installing anything.

Open editor

More from Dictionaries & sets

Related tutorials and quizzes for this topic.