Subclass dict: UserDict, dict or ABC?

If you want a custom collection that actually holds the data, subclass dict. This is especially useful if you want to extend the interface (e.g., add methods).

None of the built-in methods will call your custom __getitem__ / __setitem__, though. If you need total control over these, create a custom class that implements the collections.MutableMapping abstract base class instead.

The ABC does not provide a means to store the actual data, only an interface with default implementations for some methods. These default implementations will, however, call your custom __getitem__ and __setitem__. You will have to use an internal dict to hold the data, and implement all abstract methods: __len__, __iter__, __getitem__, __setitem__, and __delitem__.

The class UserDict from the collections module (in Python 2, the module is called UserDict as well) is a wrapper around an internal dict, implementing the MutableMapping ABC. If you want to customize the behavior of a dict, this implementation could be a starting point.

In summary:

  • MutableMapping defines the interface. Subclass this to create something that acts like a dict. It's totally up to you if and how you store the data.
  • UserDict is an implementation of MutableMapping using an internal "real" dict as storage. If you want a dict-like storage collection but override some methods exposed by dict, this might be a good starting point for you. But make sure to read the code to know how the basic methods are implemented, so that you are consistent when overriding a method.
  • dict is "the real thing". Subclass this if you want to extend the interface. Overriding methods to do custom things might be dangerous, as there are usually multiple ways of accessing the data, and you could end up with an inconsistent API.

I found an example of difference between dict and userdict from here: https://dev.to/0xbf/customize-your-own-dictionary-python-tips-5b47

If you override __delitem__ from dict, this will only be applied to del method, but not pop.

the reason why this happens is because Python's built-in dict has some inline optimizations which leads pop not calling delitem.

This is quite not intuitive.

However, when you override userdict’s __delitem__, both del and pop will be affected.