[Python-Dev] Counter proposal: multidict (was: Proposal: defaultdict)

Ian Bicking ianb at colorstudy.com
Fri Feb 17 20:51:04 CET 2006


I really don't like that defaultdict (or a dict extension) means that 
x[not_found] will have noticeable side effects.  This all seems to be a 
roundabout way to address one important use case of a dictionary with 
multiple values for each key, and in the process breaking an important 
quality of good Python code, that attribute and getitem access not have 
noticeable side effects.

So, here's a proposed interface for a new multidict object, borrowing 
some methods from Set but mostly from dict.  Some things that seemed 
particularly questionable to me are marked with ??.

class multidict:

     def __init__([mapping], [**kwargs]):
         """
         Create a multidict:

         multidict() -> new empty multidict
         multidict(mapping) -> equivalent to:
             ob = multidict()
             ob.update(mapping)
         multidict(**kwargs) -> equivalent to:
             ob = multidict()
             ob.update(kwargs)
         """

     def __contains__(key):
         """
         True if ``self[key]`` is true
         """

     def __getitem__(key):
         """
         Returns a list of items associated with the given key.  If
         nothing, then the empty list.

         ??: Is the list mutable, and to what effect?
         """

     def __delitem__(key):
         """
         Removes any instances of key from the dictionary.  Does
         not raise an error if there are no values associated.

         ??: Should this raise a KeyError sometimes?
         """

     def __setitem__(key, value):
         """
         Same as:

             del self[key]
             self.add(key, value)
         """

     def get(key, default=[]):
         """
         Returns a list of items associated with the given key,
         or if that list would be empty it returns default
         """

     def getfirst(key, default=None):
         """
         Equivalent to:
             if key in self:
                 return self[key][0]
             else:
                 return default
         """

     def add(key, value):
         """
         Adds the value with the given key, so that
         self[key][-1] == value
         """

     def remove(key, value):
         """
         Remove (key, value) from the mapping (raising KeyError if not
         present).
         """

     def discard(key, value):
         """
         Remove like self.remove(key, value), except do not raise
         KeyError if missing.
         """

     def pop(key):
         """
         Removes key and returns the value; returns [] and does nothing
         if the key is not found.
         """

     def keys():
         """
         Returns all the keys which have some associated value.
         """

     def items():
         """
         Returns [(key, value)] for every key/value pair.  Keys that
         have multiple values will be returned as multiple (key, value)
         tuples.
         """

     def __len__():
         """
         Equivalent to len(self.items())

         ??: Not len(self.keys())?
         """

     def update(E, **kwargs):
         """
         if E has iteritems then::

             for k, v in E.iteritems():
                 self.add(k, v)

         elif E has keys:

             for k in E:
                 self.add(k, E[k])

         else:

             for k, v in E:
                 self.add(k, v)

         ??: Should **kwargs be allowed?  If so, should it the values
         be sequences?
         """

     # iteritems, iterkeys, iter, has_key, copy, popitem, values, clear
     # with obvious implementations


More information about the Python-Dev mailing list