creating a defaultlist in python
On the example you give, you first try to retrieve a non-existing value on the list, as you do dl[2]['a']
, Python first retrieve the third (index 2) element on the list, then proceed to get the element named 'a' on that object - therefore you have to implement your automatic extending behavior to the __getitem__
method as well, like this:
class defaultlist(list): def __init__(self, fx): self._fx = fx def _fill(self, index): while len(self) <= index: self.append(self._fx()) def __setitem__(self, index, value): self._fill(index) list.__setitem__(self, index, value) def __getitem__(self, index): self._fill(index) return list.__getitem__(self, index)
There is a python package available:
$ pip install defaultlist
Added indicies are filled with None by default.
>>> from defaultlist import defaultlist>>> l = defaultlist()>>> l[]>>> l[2] = "C">>> l[None, None, 'C']>>> l[4]>>> l[None, None, 'C', None, None]
Slices and negative indicies are supported likewise
>>> l[1:4][None, 'C', None]>>> l[-3]'C'
Simple factory functions can be created via lambda.
>>> l = defaultlist(lambda: 'empty')>>> l[2] = "C">>> l[4]'empty'>>> l['empty', 'empty', 'C', 'empty', 'empty']
It is also possible to implement advanced factory functions:
>>> def inc():... inc.counter += 1... return inc.counter>>> inc.counter = -1>>> l = defaultlist(inc)>>> l[2] = "C">>> l[0, 1, 'C']>>> l[4]4>>> l[0, 1, 'C', 3, 4]
See the Documentation for any further details.
One could implement a defaultlist
that inherits from the MutableSequence
abstract base class and wraps around the defaultdict
. I do so in my coinflip package and expose it in the coinflip.collections
submodule.
One would need to override the ABC like so:
class defaultlist(MutableSequence): def __getitem__(self, i): ... def __setitem__(self, i, value): ... def __delitem__(self, i): ... def __len__(self): ... def insert(self): ...
I would initialise this defaultlist
by mirroring the defaultdict(default_factory=None)
method, passing default_factory
to an internal private defaultdict
.
Like with c0fec0de's solution, I would recommend filling the indices with None
by default (i.e. pass in a "none factory" method), otherwise you will get a KeyError
using unaccessed indices.
The accessor methods (get, set and delete items) would have indices just be keys in the private defaultdict
, and update that dictionary accordingly when items are added and/or removed.
There are lesser-used aspects of the builtin list
which, if one wishes to emulate, need some special considerations. I wrote more in-depth about the subject here.