Как использовать конкретную структуру данных в качестве default_factory для defaultdict?

Я в настоящее время использую defaultdict из Counter чтобы однозначно подсчитать несколько непредсказуемых значений для непредсказуемых ключей:

from collections import defaultdict, Counter

d = defaultdict(Counter)
d['x']['b'] += 1
d['x']['c'] += 1
print(d)

Это дает мне ожидаемый результат:

defaultdict(<class 'collections.Counter'>, {'x': Counter({'c': 1, 'b': 1})})

Теперь мне нужно расширить структуру значений в defaultdict и сделать это dict с двумя ключами: предыдущий Counter и str:

mystruct = {
    'counter': collections.Counter(),
    'name': ''
}

Можно ли использовать конкретную структуру данных (например, выше) в качестве default_factory в defaultdict? Ожидаемый результат будет таким, что для каждого несуществующего ключа в defaultdict, новый ключ и значение, инициализированные с вышеупомянутой структурой, будут созданы.

2 ответа

Решение

Вам просто нужно определить свой default_factory как функцию, которая возвращает словарь, в который вы хотите установить значение по умолчанию:

from collections import defaultdict, Counter
d = defaultdict(lambda: {'counter': Counter(), 'name': ''})
d['x']['counter']['b'] += 1
d['x']['counter']['c'] += 1
print(d)

Если вы не знакомы с лямбдами, это то же самое, что и:

def my_factory():
    aDict = {'counter': Counter(), 'name':''}
    return aDict
d = defaultdict(my_factory)

Альтернативное решение ответа drootang- использовать пользовательский класс:

from collections import defaultdict, Counter

class NamedCounter:
    def __init__(self, name = '', counter = None):
        self.counter = counter if counter else Counter()
        self.name = name

    def __repr__(self):
        return 'NamedCounter(name={}, counter={})'.format(
                repr(self.name), repr(self.counter))

d = defaultdict(NamedCounter)
d['x'].counter['b'] += 1
d['x'].counter['b'] += 1
d['x'].name = 'X counter'
print(d)

defaultdict (, {'x': NamedCounter (name = 'X counter', counter = Counter ({'b': 2}))})

Кроме того, вы можете продлить Counter включить имя в сам счетчик:

from collections import defaultdict, Counter

class NamedCounter(Counter):
    def __init__(self, name = '', dict = None):
        super(Counter, self).__init__(dict if dict else {})
        self.name = name

    def __repr__(self):
        return 'NamedCounter(name={}, dict={})'.format(
                repr(self.name), super(Counter, self).__repr__())

d = defaultdict(NamedCounter)
d['x']['b'] += 1
d['x']['b'] += 1
d['x'].name = 'X counter'
print(d)

defaultdict (, {'x': NamedCounter (name = 'X counter', dict = {'b': 2})})

Другие вопросы по тегам