use cases for a defaultdict

Collapse
This topic is closed.
X
X
 
  • Time
  • Show
Clear All
new posts
  • Steven Bethard

    #1

    use cases for a defaultdict

    Steven Bethard wrote:[color=blue]
    > Agreed. I really hope that Python 3.0 applies Raymond Hettinger's
    > suggestion "Improved default value logic for Dictionaries" from
    > http://wiki.python.org/moin/Python3%2e0Suggestions
    >
    > This would allow you to make the setdefault() call only once, instead
    > of on every lookup:
    >
    > class meh(dict):
    > def __init__(self, *args, **kwargs):
    > super(meh, self).__init__( *args, **kwargs)
    > self.setdefault (function=meh)[/color]

    Steve Holden wrote:[color=blue]
    > In fact, why not go one better and also add a "default" keyword
    > parameter to dict()?[/color]

    Steven Bethard wrote:[color=blue]
    > It's not backwards compatible:
    >[color=green][color=darkred]
    > >>> dict(default=4)[/color][/color]
    > {'default': 4}[/color]

    Steve Holden wrote:[color=blue]
    > Nyargle. Thanks, you're quite right, of course: I was focussing on the
    > list-of-pairs argument style when I wrote that. So the best we could
    > do is provide a subtype, defaultdict(def ault, *args, *kw).[/color]

    Steven D'Aprano wrote:[color=blue]
    > I vote to leave dict just as it is, and add a subclass, either in a
    > module or as a built in (I'm not fussed either way) for
    > dicts-with-defaults.[/color]

    Yeah, I like that idea too. That's a lot of Steven's agreeing on this
    -- do we realy need agreement from people with other names too? ;)

    I do think defaultdict() is probably the right way to go, though I'm not
    certain about the signature -- it needs to support value-based defaults
    (e.g. 0) and function based defaults (e.g. a new list each time).
    Perhaps we need to introduce two new collections objects,
    defaultvaluedic t and defaultfunction dict:
    [color=blue][color=green][color=darkred]
    >>> class defaultdict(dic t):[/color][/color][/color]
    .... def __init__(self, default, *args, **kwargs):
    .... super(defaultdi ct, self).__init__( *args, **kwargs)
    .... self._default = default
    .... def __repr__(self):
    .... type_name = type(self).__na me__
    .... super_str = super(defaultdi ct, self).__repr__( )
    .... return '%s(%r, %s)' % (type_name, self._default, super_str)
    ....[color=blue][color=green][color=darkred]
    >>> class defaultvaluedic t(defaultdict):[/color][/color][/color]
    .... def __getitem__(sel f, key):
    .... if key not in self:
    .... self[key] = self._default
    .... return super(defaultva luedict, self).__getitem __(key)
    ....[color=blue][color=green][color=darkred]
    >>> class defaultfunction dict(defaultdic t):[/color][/color][/color]
    .... def __getitem__(sel f, key):
    .... if key not in self:
    .... self[key] = self._default()
    .... return super(defaultfu nctiondict, self).__getitem __(key)
    ....[color=blue][color=green][color=darkred]
    >>> counts = defaultvaluedic t(0)
    >>> counts['Steve'] += 1
    >>> counts['Steve'] += 1
    >>> counts['Steven'] += 1
    >>> counts[/color][/color][/color]
    defaultvaluedic t(0, {'Steve': 2, 'Steven': 1})[color=blue][color=green][color=darkred]
    >>> groups = defaultfunction dict(list)
    >>> groups['Steve'].append('Holden ')
    >>> groups['Steve'].append('Bethar d')
    >>> groups['Steven'].append("D'Apra no")
    >>> groups[/color][/color][/color]
    defaultfunction dict(<type 'list'>, {'Steve': ['Holden', 'Bethard'],
    'Steven': ["D'Aprano"]})

    I didn't override of get() or setdefault(), which means they don't use
    the default associated with the dict. I think this is right because the
    only time you'd use get() or setdefault() with a defaultdict is if you
    wanted to override the default normally associated with it.

    The question, I guess, is whether or not there are enough use cases to
    merit introducing these types into the collections module. Some of my
    use cases:

    * Counting numbers of items, using defaultvaluedic t(0). Currently, I
    support this by having a counts() function in my dicttools module:

    def counts(iterable , key=None):
    result = {}
    for item in iterable:
    # apply key function if necessary
    if key is None:
    k = item
    else:
    k = key(item)
    # increment key's count
    try:
    result[k] += 1
    except KeyError:
    result[k] = 1
    return result

    Raymond Hettinger has also proposed a bag_ recipe for similar purposes.

    ... _bag: http://aspn.activestate.com/ASPN/Coo.../Recipe/259174

    * Grouping items into lists, using defaultfunction dict(list).
    Currently, I support this by having a groupby() function in my dicttools
    module:

    def groupby(iterabl e, key=None, value=None):
    result = {}
    for item in iterable:
    # apply key function if necessary
    if key is None:
    k = item
    else:
    k = key(item)
    # apply value function if necessary
    if value is None:
    v = item
    else:
    v = value(item)
    # append value to key's list
    try:
    result[k].append(v)
    except KeyError:
    result[k] = [v]
    return result

    Note that for both of my use cases, the appropriate defaultdict() could
    take the try/except (equivalent to a setdefault() call) out of my code.
    It's nice, but not a huge gain -- I definitely can't drop my code
    completely...

    Do others have compelling use-cases for a defaultdict-like class?

    STeVe
Working...