Есть ли умный способ передать ключ в default_factory defaultdict? - PullRequest
75 голосов
/ 26 мая 2010

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

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

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

d = defaultdict(lambda : C(here_i_wish_the_key_to_be))

Есть предложения?

Ответы [ 3 ]

103 голосов
/ 26 мая 2010

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

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)
20 голосов
/ 29 января 2016

Нет, нет.

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

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

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

6 голосов
/ 26 мая 2010

Я не думаю, что вам нужно 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-exc:

>>> 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
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...