Links
Implementing / subclassing dicts and sets.
Note: Using the UserDict.DictMixin
method is deprecated.
New code should inherit from the collections.MutableMapping
abstract base class.
Links
collections.MutableMapping
- OrderedSet recipe – based on MutableSet.
- StackOverflow: How to perfectly override a dict
Implementing your own Set
From collections.MutableMapping
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()
class ListBasedSet(collections.Set): ''' Alternate set implementation favoring space over speed and not requiring the set elements to be hashable. ''' def __init__(self, iterable): self.elements = lst = [] for value in iterable: if value not in lst: lst.append(value) def __iter__(self): return iter(self.elements) def __contains__(self, value): return value in self.elements def __len__(self): return len(self.elements) s1 = ListBasedSet('abcdef') s2 = ListBasedSet('defghi') overlap = s1 & s2 # The __and__() method is supported automatically
Notes on using Set and MutableSet as a mixin:
- Since some set operations create new sets, the default
mixin methods need a way to create new instances from an
iterable. The class constructor is assumed to have a
signature in the form
ClassName(iterable)
. That assumption is factored-out to an internal classmethod called_from_iterable()
which callscls(iterable)
to produce a new set. If the Set mixin is being used in a class with a different constructor signature, you will need to overridefrom_iterable()
with a classmethod that can construct new instances from an iterable argument. - To override the comparisons (presumably for speed, as
the semantics are fixed), redefine
__le__()
and then the other operations will automatically follow suit. - The Set mixin provides a
_hash()
method to compute a hash value for the set; however,__hash__()
is not defined because not all sets are hashable or immutable. To add set hashabilty using mixins, inherit from bothSet()
andHashable()
, then define__hash__
=Set._hash
.
Implementing your own dict
From StackOverflow: How to perfectly override a dict
import collections class TransformedDict(collections.MutableMapping): """A dictionary which applies an arbitrary key-altering function before accessing the keys""" def __init__(self, *args, **kwargs): self.store = dict() self.update(dict(*args, **kwargs)) # use the free update to set keys def __getitem__(self, key): return self.store[self.__keytransform__(key)] def __setitem__(self, key, value): self.store[self.__keytransform__(key)] = value def __delitem__(self, key): del self.store[self.__keytransform__(key)] def __iter__(self): return iter(self.store) def __len__(self): return len(self.store) def __keytransform__(self, key): return key
You get a few free methods from the ABC (Abstract Base Classes)
class MyTransformedDict(TransformedDict): def __keytransform__(self, key): return key.lower() s = MyTransformedDict( [('Test', 'test')]) assert s.get('TEST') is s['test'] # free get assert 'TeSt' in s # free __contains__ # free setdefault, __eq__, and so on import pickle assert pickle.loads(pickle.dumps( s )) == s #works too since we just use a normal dict
I wouldn't subclass dict (or other builtins) directly. It often makes no sense, because what you actually want to do is implement the interface of a dict. And that is exactly what ABCs are for.