Есть ли разумный способ передать ключ defaultdict default_factory?

В классе есть конструктор, который принимает один параметр:

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

Где-то в коде полезно для значений в dict знать свои ключи.
Я хочу использовать defaultdict с ключом, переданным значениям по умолчанию для новорожденных:

d = defaultdict(lambda : C(here_i_wish_the_key_to_be))

Любые предложения?

Ответ 1

Он вряд ли квалифицируется как умный, но подклассификация - ваш друг:

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

d = keydefaultdict(C)
d[x] # returns C(x)

Ответ 2

Нет, нет.

Реализация defaultdict не может быть настроена на передачу отсутствующего key в default_factory из коробки. Ваш единственный вариант - реализовать свой собственный подкласс defaultdict, как было предложено @JochenRitzel, выше.

Но это не "умный" или почти такой же чистый, как стандартное решение библиотеки (если оно существует). Таким образом, ответ на ваш сжатый, да/нет вопрос явно "нет".

Слишком плохо, что в стандартной библиотеке отсутствует такой часто требуемый инструмент.

Ответ 3

Я не думаю, что вам нужно defaultdict здесь вообще. Почему бы просто не использовать метод dict.setdefault?

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

Это, конечно же, создаст много экземпляров C. Если это проблема, я думаю, что более простой подход будет делать:

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

Это было бы быстрее, чем defaultdict или любой другой альтернативы, насколько я могу видеть.

ETA относительно скорости теста in против использования предложения try-except:

>>> 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