Существует ли стандартный класс для бесконечно вложенного defaultdict? - PullRequest
11 голосов
/ 14 ноября 2010

Кто-нибудь знает, есть ли стандартный класс для бесконечно нестабильного словаря в Python?

Я повторяю этот шаблон:

d = defaultdict(lambda: defaultdict(lambda: defaultdict(int)))
d['abc']['def']['xyz'] += 1

Если я хочу добавить «еще один слой» (например, d['abc']['def']['xyz']['wrt']), я должен определить другое вложение defaultdicts.

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

1012 *, например *

d = InfiniteDict(('count',0),('total',0))
d['abc']['def']['xyz'].count += 0.24
d['abc']['def']['xyz'].total += 1
d['abc']['def']['xyz']['wrt'].count += 0.143
d['abc']['def']['xyz']['wrt'].total += 1

Однако кто-нибудь знает о ранее существовавшей реализации этой идеи? Я пробовал поискать в Google, но я не уверен, как это будет называться.

Ответы [ 6 ]

14 голосов
/ 14 ноября 2010

Это естественно для рекурсивного определения.

>>> import collections
>>> def nested_dd():
...     return collections.defaultdict(nested_dd)
...
>>> foo = nested_dd()
>>> foo
defaultdict(<function nested_dd at 0x023F0E30>, {})
>>> foo[1][2]=3
>>> foo[1]
defaultdict(<function nested_dd at 0x023F0E30>, {2: 3})
>>> foo[1][2]
3
11 голосов
/ 14 ноября 2010

Вы можете получить от defaultdict, чтобы получить желаемое поведение:

class InfiniteDict(defaultdict):
   def __init__(self):
      defaultdict.__init__(self, self.__class__)

class Counters(InfiniteDict):
   def __init__(self):
      InfiniteDict.__init__(self)                                               
      self.count = 0
      self.total = 0

   def show(self):
      print "%i out of %i" % (self.count, self.total)

Использование этого класса будет выглядеть так:

>>> d = Counters()
>>> d[1][2][3].total = 5
>>> d[1][2][3].show()
0 out of 5
>>> d[5].show()
0 out of 0
8 голосов
/ 10 июня 2013

Я думаю, что это однострочник - почти идеальное решение:

>>> from collections import defaultdict
>>> infinite_defaultdict = lambda: defaultdict(infinite_defaultdict)
>>> d = infinite_defaultdict() 
>>> d['x']['y']['z'] = 10

Рэймонд Хеттингер в Твиттере (https://twitter.com/raymondh/status/343823801278140417)

3 голосов
/ 14 ноября 2010

Идеальное решение, вдохновленное ответом sth:

from collections import defaultdict

class InfiniteDict(defaultdict):
   def __init__(self, **kargs):
      defaultdict.__init__(self, lambda: self.__class__(**kargs))
      self.__dict__.update(kargs)

d = InfiniteDict(count=0, total=0)
d['abc']['def'].count += 0.25
d['abc']['def'].total += 1
print d['abc']['def'].count
print d['abc']['def'].total
d['abc']['def']['xyz'].count += 0.789
d['abc']['def']['xyz'].total += 1
print d['abc']['def']['xyz'].count
print d['abc']['def']['xyz'].total
1 голос
/ 25 сентября 2018

В случае, если через восемь лет вы все еще думаете о том, как получить это с помощью одной строки:

from collections import defaultdict

t = defaultdict(lambda: defaultdict(t.default_factory))
0 голосов
/ 14 ноября 2010

Это близко:

class recursivedefaultdict(defaultdict):
    def __init__(self, attrFactory=int):
        self.default_factory = lambda : type(self)(attrFactory)
        self._attrFactory = attrFactory
    def __getattr__(self, attr):
        newval = self._attrFactory()
        setattr(self, attr, newval)
        return newval

d = recursivedefaultdict(float)
d['abc']['def']['xyz'].count += 0.24  
d['abc']['def']['xyz'].total += 1  

data = [
    ('A','B','Z',1),
    ('A','C','Y',2),
    ('A','C','X',3),
    ('B','A','W',4),
    ('B','B','V',5),
    ('B','B','U',6),
    ('B','D','T',7),
    ]

table = recursivedefaultdict(int)
for k1,k2,k3,v in data:
    table[k1][k2][k3] = v

Это не совсем то, что вы хотите, так как самый глубоко вложенный уровень не имеет ваших значений по умолчанию 0 для 'count' или 'total'.

Отредактировано: Ах, теперь это работает - просто нужно добавить метод __getattr__, и он делает то, что вы хотите.

Редактировать 2: Теперь вы можете определить другие фабричные методы дляатрибуты, кроме целыхНо все они должны быть одного типа, их число не должно быть float, а total быть int.

...