tags:

views:

71

answers:

2

A class has a constructor which takes one parameter:

class C(object):
    def __init__(self, v):
        self.v = v
        ...

Somewhere in the code, it is useful for values in a dict to know their keys.
I want to use a defaultdict with the key passed to newborn default values:

d = defaultdict(lambda : C(here_i_wish_the_key_to_be))

Any suggestions?

+1  A: 

It hardly qualifies as clever - but subclassing is your friend:

class keydefaultdict(defaultdict):
    def __missing__(self, key):
        if self.default_factory is None:
            raise KeyError( key )
        else:
            ret = self[key] = self.default_factory(key)
            return ret
THC4k
That's exactly the uglyness I'm trying to avoid... Even using a simple dict and checking for key existence is much cleaner.
Paul Oyster
@Paul: and yet this is your answer. Ugliness? Come on!
ΤΖΩΤΖΙΟΥ
A: 

I don't think you need defaultdict here at all. Why not just use dict.setdefault method?

>>> d = {}
>>> d.setdefault('p', C('p')).v
'p'

That will of course would create many instances of C. In case it's an issue, I think the simpler approach will do:

>>> d = {}
>>> if 'e' not in d: d['e'] = C('e')

It would be quicker than the defaultdict or any other alternative as far as I can see.

ETA regarding the speed of in test vs. using try-except clause:

>>> def g():
    d = {}
    if 'a' in d:
        return d['a']


>>> timeit.timeit(g)
0.19638929363557622
>>> def f():
    d = {}
    try:
        return d['a']
    except KeyError:
        return


>>> timeit.timeit(f)
0.6167065411074759
>>> def k():
    d = {'a': 2}
    if 'a' in d:
        return d['a']


>>> timeit.timeit(k)
0.30074866358404506
>>> def p():
    d = {'a': 2}
    try:
        return d['a']
    except KeyError:
        return


>>> timeit.timeit(p)
0.28588609450770264
SilentGhost
This is highly wasteful in cases where d is accessed many times, and only rarely missing a key: C(key) will thus create tons of unneeded objects for the GC to collect. Also, in my case there is an additional pain, since creating new C objects is slow.
Paul Oyster
@Paul: that's right. I would suggest then even more simple method, see my edit.
SilentGhost
I'm not sure it is quicker than defaultdict, but this is what I usually do (see my comment to THC4k's answer). I hoped there is a simple way to hack around the fact default_factory takes no args, to keep the code slightly more elegant.
Paul Oyster
@Paul: of course it's faster! it's a single `in` statement! It is also clean and readable. `defaultdict` has just different intention behind it.
SilentGhost
it is an 'if k in d' vs. (a hidden) 'try: d[k] except KeyError'; CPython's implementation is very fast with exceptions, so should be on the same speed level.
Paul Oyster
@Paul: you understand that these are different pieces of coded, right? Additionally, `in` would always be faster that the try-except clause.
SilentGhost
Exceptions are as fast as tests. This is one of the reasons BTAFTP exists alongside LBYL. (Although it turned out to be implementation-specific: IronPython is extremely slow with exceptions, due to .NET design).
Paul Oyster
@Paul: see my edit
SilentGhost