Python: defaultdict из defaultdict? - PullRequest
       8

Python: defaultdict из defaultdict?

270 голосов
/ 17 февраля 2011

Есть ли способ получить defaultdict(defaultdict(int)), чтобы заставить работать следующий код?

for x in stuff:
    d[x.a][x.b] += x.c_int

d должен быть построен ad-hoc в зависимости от x.a и x.b элементов.

Я мог бы использовать:

for x in stuff:
    d[x.a,x.b] += x.c_int

но тогда я не смогу использовать:

d.keys()
d[x.a].keys()

Ответы [ 5 ]

488 голосов
/ 17 февраля 2011

Да, вот так:

defaultdict(lambda: defaultdict(int))

Аргумент defaultdict (в данном случае lambda: defaultdict(int)) будет вызываться при попытке доступа к ключу, который не существует.Его возвращаемое значение будет установлено как новое значение этого ключа, что означает, что в нашем случае значение d[Key_doesnt_exist] будет defaultdict(int).

Если вы попытаетесь получить доступ к ключу из этого последнего defaultdictт.е. d[Key_doesnt_exist][Key_doesnt_exist] вернет 0, что является возвращаемым значением аргумента последнего defaultdict, т.е. int().

47 голосов
/ 17 февраля 2011

Параметром конструктора defaultdict является функция, которая будет вызываться для создания новых элементов.Так что давайте использовать лямбду!

>>> from collections import defaultdict
>>> d = defaultdict(lambda : defaultdict(int))
>>> print d[0]
defaultdict(<type 'int'>, {})
>>> print d[0]["x"]
0

Начиная с Python 2.7, есть еще лучшее решение с использованием счетчика :

>>> from collections import Counter
>>> c = Counter()
>>> c["goodbye"]+=1
>>> c["and thank you"]=42
>>> c["for the fish"]-=5
>>> c
Counter({'and thank you': 42, 'goodbye': 1, 'for the fish': -5})

Некоторые бонусные функции

>>> c.most_common()[:2]
[('and thank you', 42), ('goodbye', 1)]

Для получения дополнительной информации см. PyMOTW - Коллекции - Типы данных контейнера и Документация Python - коллекции

28 голосов
/ 17 февраля 2011

Я считаю, что немного более элегантно использовать partial:

import functools
dd_int = functools.partial(defaultdict, int)
defaultdict(dd_int)

Конечно, это то же самое, что лямбда.

6 голосов
/ 17 февраля 2011

Другие правильно ответили на ваш вопрос о том, как заставить работать следующее:

for x in stuff:
    d[x.a][x.b] += x.c_int

Альтернативой может быть использование кортежей для ключей:

d = defaultdict(int)
for x in stuff:
    d[x.a,x.b] += x.c_int
    # ^^^^^^^ tuple key

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

4 голосов
/ 19 января 2019

Для справки, можно реализовать универсальный вложенный defaultdict фабричный метод через:

from collections import defaultdict
from functools import partial
from itertools import repeat


def nested_defaultdict(default_factory, depth=1):
    result = partial(defaultdict, default_factory)
    for _ in repeat(None, depth - 1):
        result = partial(defaultdict, result)
    return result()

Глубина определяет количество вложенных словарей перед использованием типа, определенного в default_factory.Например:

my_dict = nested_defaultdict(list, 3)
my_dict['a']['b']['c'].append('e')
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...