50

What's the difference between UserDict, dict and ABC and which one is recommended? The docs seem to deprecate UserDict?

Also it seems UserDict's update() would use my setitem method whereas dict doesn't? Which methods are really essential to override given I want custom setitem and getitem function?

With ABCs I'd have to implement absolutely all methods since it provides no default implementation?

I want to make a dict that does two things:

  • intern() all keys and values
  • store some of the values in an SQLite database

So which of UserDict, dict and ABC would best allow me to do this?

murgatroid99
  • 15,284
  • 8
  • 52
  • 88
Gerenuk
  • 10,513
  • 16
  • 48
  • 83
  • 1
    But at least some ABCs are also useful to simplify implementations that aren't based on built-in types, as this snippet from the same section shows: `Several of the ABCs are also useful as mixins that make it easier to develop classes supporting container APIs. For example, to write a class supporting the full Set API, it only necessary to supply the three underlying abstract methods: __contains__(), __iter__(), and __len__(). The ABC supplies the remaining methods such as __and__() and isdisjoint()` –  Aug 22 '11 at 14:10
  • 2
    If you want to test if something is a `dict`, use `isistance(x, dict)`. If you want to test it it's some kind of mapping, use `isinstance(x, Mapping)`. This is what the ABCs are there for. They are only useful if every kind of mapping uses `Mapping.register()` or just subclasses it directly. And yes, this **is** the primary purpose of an ABC. – Ferdinand Beyer Aug 22 '11 at 14:12
  • similar: https://stackoverflow.com/q/1392396, https://stackoverflow.com/q/3387691 – djvg Apr 09 '21 at 13:56

4 Answers4

63

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.
Ferdinand Beyer
  • 58,119
  • 13
  • 142
  • 141
  • Thanks. From the previous post I learned about Mixins and now I realized that ABC provide default implementations. It's a minor issue to provide a data variable myself, so I suppose I will get used to ABC. – Gerenuk Aug 22 '11 at 14:02
  • @agf: Even `DictMixin` requires you to implement 5 base methods. – Ferdinand Beyer Aug 22 '11 at 14:05
  • @agf: The `userdict` module is gone in Python 3.0, this is why I said it's deprecated. See http://www.python.org/dev/peps/pep-3108/ (Quote: "Not as useful since types can be a superclass. Useful bits moved to the 'collections' module.") – Ferdinand Beyer Aug 22 '11 at 14:07
  • Sorry if I missed something, but UserDict is still here in Python 3: https://docs.python.org/3/library/collections.html#collections.UserDict And it is useful if you need to have a working `update` method. – Adrien Clerc Dec 05 '14 at 15:43
  • 2
    @AdrienClerc The `UserDict` _module_ is gone, the _class_ was moved to `collections` and made implement the `MutableMapping` ABC. Please note that the `update` method is provided by `MutableMapping`, not `UserDict`, so that alone is no reason to use `UserDict`. You should use `UserDict` if you want a wrapper for an internal `dict`. If you just want to simulate a `dict` and store your data elsewhere, use `MutableMapping`. – Ferdinand Beyer Dec 05 '14 at 18:10
  • Python2: [MutableMapping](https://docs.python.org/2.7/library/collections.html?highlight=mutablemapping#collections.MutableMapping) – Mr_and_Mrs_D May 28 '15 at 22:33
  • 3
    Suggesting someone subclass dict because you think `UserDict` is gone in Python 3 is wrong. Like others said, `UserDict` is alive and well in Python 3, and AFAIK recommended over subclassing `dict` itself. – ErlVolton Sep 24 '15 at 23:00
1

Based on Secrets Recipes of the Python Ninja book

The only special thing the UserDict has beyond the normal dictionary operations is a single attribute:

data: A real dictionary to hold the contents of the UserDict class

To get to the items in the dictionary, you have to either iterate over them or call items(). While the UserDict instance supports the same methods, the view returned by items() is noticeably different:

  >>> from collections import UserDict
  >>> a = UserDict(a=1)
  >>> d = dict(d=3)  # regular dictionary for comparison

  >>> for k in d:
  ...     print(k, d[k])
  ... 
  d 3
  >>> d.items()
  dict_items([('d', 3)])
  >>> for k in a:
  ...     print(k, a[k])
  ... 
  a 1
  >>> a.items()
  ItemsView({'a': 1})

Notice that the dictionary object returns a tuple of key/values. The UserDict returns an actual dictionary object. Depending on what you are doing, this difference can be important, as is the ability to use the data attribute to access the dictionary.

Vlad Bezden
  • 59,971
  • 18
  • 206
  • 157
1

Don't use the UserDict class -- you don't need it. As the docs say, you can just subclass dict directly.

However, you still want the UserDict module, for DictMixin:

Note: DictMixin, while not officially deprecated, has been removed in Python 3, and it's recommended in the docs that you use collections.MutableMapping. This, however, has a drawback -- you need to implement more of the dictionary interface - __delitem__, __getitem__, __iter__, __len__, and __setitem__. With DictMixin, you can just implement the ones you want to change, and the rest use a default implementation.

from UserDict import DictMixin

class MyDict(DictMixin, dict):
    def __setitem__(self, key, value):
        print key, value # just an example
        # use intern(key) or whatever here
        dict.__setitem__(self, key, value) # or
        # super(MyDict, self).__setitem__(key, value)

m = MyDict()

m['a'] = 'b'
# a b
m.update({'a': 'c'})
# a c

It will automatically make update use your __setitem__ as you want.

agf
  • 148,965
  • 36
  • 267
  • 227
  • 3
    > Starting with Python version 2.6, it is recommended to use collections.MutableMapping instead of DictMixin. – cwallenpoole Aug 22 '11 at 14:00
  • 3
    @agf: You are wrong. The `UserDict` module is deprecated and shouldn't be used. How is using `UserDict.DictMixin` as a mix-in easier than using `collections.MutableMapping` as a mix-in? You will only need to overwrite `__setitem__()` in both cases. – Sven Marnach Aug 22 '11 at 14:22
  • @agf: The whole module is gone in Python 3.x. – Sven Marnach Aug 22 '11 at 14:30
  • Unfortunately, it's not that simple. If you subclass both `MutableSequence` and `dict`, `__getitem__` etc. are still abstract since the implementation in `dict` will not override them. You still have to provide custom implementations. `UserDict` (the class) still exists in Python 3, by the way -- it was moved to the `collections` module and now inherits from `MutableMapping`. – Ferdinand Beyer Aug 22 '11 at 14:38
  • I've rolled back my post to the original version, and added a note about `MutableMapping`, the docs, and Python 3. – agf Aug 22 '11 at 14:51
  • 1
    I don't think so. If you want all the functionality of a `MutableMapping`, you will have to implement all abstract methods. But there are only 5 of those, so it's not that bad. But again, since `DictMixin` is gone in Py3k, I would strongly advice against using it. With Python 3.3 coming next year, the days of 2.x are numbered. – Ferdinand Beyer Aug 22 '11 at 14:53
  • I revoked my upvote because of the rollback. It's simply not true that you need to define more methods with `MutableMapping` than with `DictMixin`. Try `class MyDict(dict, MutableMapping)` instead of `class MyDict(DictMixin, dict)`. – Sven Marnach Aug 23 '11 at 16:01
  • 2
    I did. Try that with the example in my answer. `update` won't call `__setitem__`, so you won't see `a c` printed. In fact, adding `MutableMapping` after `dict` in the method resolution order has _no effect at all_, as `dict` implements all of the methods defined as abstract by `MutableMapping`. There is no way to get the desired behavior with `MutableMapping` without defining all of the core dictionary interface. – agf Aug 23 '11 at 16:11
  • It's annoying that a MutableMapping is not an instance of dict. Really want an ABCDict so that it fools `isinstance(_, dict)` :( – Andy Hayden Mar 18 '14 at 23:51
  • Is this answer still (2020) valid? How do I reconcile this answer [with this page](http://www.kr41.net/2016/03-23-dont_inherit_python_builtin_dict_type.html), which says _not_ to subclass dict? – xdavidliu Aug 29 '20 at 19:10
  • @xdavidliu In Python 2 it really depended on your use-case. In Python 3, as already mentioned in the `Note` section of the answer, you shouldn't do this. – agf Aug 29 '20 at 22:24
  • `UserDict` still exists in Python 3 but has been moved in `collections.UserDict`. I believe it has been kept as it makes inheriting from dict much easier (only overwrite `__setitem__` will works for `__init__`, `update`, `setdefault`) – Conchylicultor Nov 02 '20 at 13:38
0

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.

Hunger
  • 4,328
  • 5
  • 18
  • 27