Pre-PEP: Dictionary accumulator methods

Duncan Booth duncan.booth at invalid.invalid
Sun Mar 20 17:29:56 CET 2005

Roose wrote:

> I think someone mentioned that it might be a problem to add another
> piece of state to all dicts though.  I don't know enough about the
> internals to say anything about this.
> setdefault gets around this by having you pass in the value every
> time, so it doesn't have to store it.  It's very similar, but somehow
> many times more awkward.

Another option with no storage overhead which goes part way to reducing
the awkwardness would be to provide a decorator class accessible through
dict. The decorator class would take a value or function to be used as
the default, but apart from __getitem__ would simply forward all other
methods through to the underlying dictionary. 

That gives you the ability to have not one default value for a
dictionary, but many different ones: you just decorate the dictionary
anywhere you need a default and use the underlying dictionary everywhere

Some code which demonstrates the principle rather than the
implementation. dictDefaultValue could be imagined as
dict.defaultValue, dictDefaultValue(d, ...) could be 
d.defaultValue(...) although the actual name used needs work:

>>> class dictDefaultValue(object):
	def __init__(self, d, value=_marker, function=_marker):
	    self.__d = d
	    if value is _marker:
	        if function is _marker:
	            raise TypeError, "expected either value or function argument"
		self.__dv = function
	        def defaultValue():
	            return value
	        self.__dv = defaultValue
	def __getattr__(self, name):
	    return getattr(self.__d, name)
	def __getitem__(self, name):
	        return self.__d[name]
	    except KeyError:
	        value = self.__dv()
	        self.__d[name] = value
	        return value
	def __setitem__(self, name, value):
	    self.__d[name] = value

>>> d = {}
>>> accumulator = dictDefaultValue(d, 0)
>>> accumulator['a'] += 1
>>> aggregator = dictDefaultValue(d, function=list)
>>> aggregator['b'].append('hello')
>>> d
{'a': 1, 'b': ['hello']}

More information about the Python-list mailing list