UserDict, UserList, UserString
Subclassing dict directly has a catch: its C methods (update, the constructor, get …) do not call your __setitem__ or __getitem__. UserDict is written in Python on top of .data, so overriding one method changes them all.
Common call
class LowerDict(UserDict): …
Returns
a dict-like object backed by self.data
Replaces
subclassing dict / list / str when overrides must apply everywhere
Watch out
isinstance(UserDict(), dict) is False
collections.UserDict([initialdata])
→ UserDict | UserList | UserString
Parameters
| Name | Type | Required | Description |
|---|---|---|---|
| initialdata | mapping | iterable | no | UserDict: initial contents (copied into a new dict in .data). Keyword arguments work too. |
| list | iterable | no | UserList: initial items (copied into .data). |
| seq | object | yes | UserString: converted with str() into .data. |
Return value
UserDict | UserList | UserString — An object that stores its contents in the .data attribute (a dict, list or str).
Attributes
| Attribute | Type | Meaning |
|---|---|---|
| data | dict | list | str | The real contents. Methods read and write it; you can too. |
Common patterns
A dict that normalises keys
Override __setitem__ once — update() and the constructor use it too.
from collections import UserDict class LowerDict(UserDict): def __setitem__(self, key, value): super().__setitem__(key.lower(), value)
A list that validates items
UserList methods such as append go through .data; override the ones you need.
from collections import UserList class IntList(UserList): def append(self, item): if not isinstance(item, int): raise TypeError("ints only") super().append(item)
Reach the raw data
.data is a plain dict / list / str.
plain = my_user_dict.data
Examples
1. Overrides apply to update() too
from collections import UserDict
class LowerDict(UserDict):
def __setitem__(self, key, value):
super().__setitem__(key.lower(), value)
d = LowerDict({'A': 1})
d.update(B=2)
d.data
Returns
{'a': 1, 'b': 2}2. A dict subclass skips them
class LowerDict(dict):
def __setitem__(self, key, value):
super().__setitem__(key.lower(), value)
d = LowerDict({'A': 1})
d.update(B=2)
d
Returns
{'A': 1, 'B': 2}3. Not a dict instance
from collections import UserDict
isinstance(UserDict(), dict)
Returns
False4. But a Mapping
from collections import UserDict
from collections.abc import MutableMapping
isinstance(UserDict(), MutableMapping)
Returns
True5. UserList keeps a list in .data
from collections import UserList
ul = UserList([3, 1, 2])
ul.sort()
(ul, type(ul.data).__name__)
Returns
([1, 2, 3], 'list')6. UserString methods return UserString
from collections import UserString
type(UserString('abc').upper()).__name__
Returns
'UserString'Pitfalls
1. Subclassing dict and overriding __setitem__
dict's own constructor and update() bypass the override, so some keys slip through unnormalised.
class X(dict)
class Upper(dict): def __setitem__(self, k, v): super().__setitem__(k.upper(), v) Upper(a=1)
{'a': 1}
class X(UserDict)
from collections import UserDict class Upper(UserDict): def __setitem__(self, k, v): super().__setitem__(k.upper(), v) Upper(a=1).data
{'A': 1}
2. Code that checks isinstance(x, dict)
A UserDict is not a dict. Check against collections.abc.Mapping instead, or pass .data.
isinstance(dict)
from collections import UserDict isinstance(UserDict(a=1), dict)
False
isinstance(Mapping)
from collections import UserDict from collections.abc import Mapping isinstance(UserDict(a=1), Mapping)
True
When to use
Use it
- Custom containers whose overridden methods must be used consistently
- Wrapping a dict/list/str with extra behaviour (validation, normalisation, logging)
Reach for something else
- Only adding new methods (not overriding) → subclass dict/list/str directly and keep isinstance(x, dict)
- A full custom mapping from scratch → collections.abc.MutableMapping
Notes
CPython impl
Lib/collections/__init__.py — pure Python; UserDict is a MutableMapping, UserList a MutableSequence, UserString a Sequence, each delegating to self.data
No demo
The interesting part is subclassing — class bodies with overrides — which a form input cannot express; the examples show it instead
FAQ
UserDict when you override methods like __setitem__ or __getitem__ and need every other method (update, setdefault, the constructor) to respect them. dict when you only add new methods — you skip the Python-level delegation and pass isinstance(x, dict).