collections.UserList is a helper class for building custom mutable sequences. It stores elements in a regular list exposed through data and offers a predictable extension surface for validation, normalization, logging, and domain rules.
It is useful when a collection should behave like a list but must control insertions, replacements, deletion, accepted types, or batch operations.
Basic example
from collections import UserList
class IntegerList(UserList):
def _validate(self, value):
if not isinstance(value, int):
raise TypeError("integers only")
return value
def append(self, value):
super().append(self._validate(value))
def insert(self, index, value):
super().insert(index, self._validate(value))
Validating only append is not enough. A list can also change through index assignment, slices, extend, and +=.
Covering mutation paths
class IntegerList(UserList):
def _validate(self, value):
if not isinstance(value, int):
raise TypeError("integers only")
return value
def __setitem__(self, index, value):
if isinstance(index, slice):
value = [self._validate(item) for item in value]
else:
value = self._validate(value)
super().__setitem__(index, value)
def append(self, value):
super().append(self._validate(value))
def insert(self, index, value):
super().insert(index, self._validate(value))
def extend(self, values):
super().extend(self._validate(item) for item in values)
The data attribute
data contains the actual list. Direct changes can bypass validation, so treat it as an implementation detail.
Normalizing values
class Tags(UserList):
def _normalize(self, value):
text = str(value).strip().casefold()
if not text:
raise ValueError("empty tag")
return text
Decide whether duplicates are valid. If uniqueness is the main rule, a set or mapping may model the domain better.
UserList versus list subclassing
A direct list subclass may be faster and can be required by APIs expecting the concrete type. UserList prioritizes extensibility and routes operations through methods that are easier to customize.
UserList versus MutableSequence
Implement MutableSequence when storage is not an ordinary list, such as disk-backed data, a virtual window, or a compact structure. Choose UserList when an internal list is sufficient.
Operations returning new sequences
Test concatenation, multiplication, slicing, and copying. Define whether results should preserve the custom class or become normal lists.
Sorting and instrumentation
class Tasks(UserList):
def sort(self, *, key=None, reverse=False):
log("sorting tasks")
super().sort(key=key, reverse=reverse)
Avoid expensive or surprising side effects in familiar list operations.
Serialization
Some serializers require a concrete list. Convert explicitly:
import json
json.dumps(list(values))
Common mistakes
- Validating only
append. - Ignoring slice assignment and
extend. - Changing
datadirectly. - Using a list when uniqueness is required.
- Assuming the result type of slicing or concatenation.
Recommended practice
Inventory every mutation path, centralize validation, call super(), test operators, and document result types. See the internal guides to Python lists and collections.
Conclusion
UserList is a practical base for custom sequences backed by a regular list. It helps enforce consistent invariants without depending on list implementation details.







