Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

creating a defaultlist in python

I'm trying to create a list equivalent for the very useful collections.defaultdict. The following design works nicely:

class defaultlist(list):
    def __init__(self, fx):
        self._fx = fx
    def __setitem__(self, index, value):
        while len(self) <= index:
            self.append(self._fx())
        list.__setitem__(self, index, value)

Here's how you use it:

>>> dl = defaultlist(lambda:'A')
>>> dl[2]='B'
>>> dl[4]='C'
>>> dl
['A', 'A', 'B', 'A', 'C']

What should I add to the defaultlist so as to support the following behavior?

>>> dl = defaultlist(dict)
>>> dl[2]['a'] = 1
>>> dl
[{}, {}, {'a':1}]
like image 638
Jonathan Livni Avatar asked Jan 05 '12 20:01

Jonathan Livni


2 Answers

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)
like image 99
jsbueno Avatar answered Oct 25 '22 00:10

jsbueno


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.

like image 42
c0fec0de Avatar answered Oct 25 '22 01:10

c0fec0de