1

我想有一本深度嵌套的字典。让我们“深刻地”考虑一下。为了显示我需要一个 5 级字典,例如,foo[1][2][3][4][5]将有一个setlist作为项目。

正如我在这里看到的,我至少可以通过两种方式实现这一点:

from collections import defaultdict
foo = defaultdict(lambda: defaultdict(lambda:defaultdict(lambda: defaultdict(lambda: defaultdict(set)))))

或者

from functools import partial
foo = defaultdict(partial(defaultdict, partial(defaultdict, partial(defaultdict, partial(defaultdict, set)))))

然后在这两种情况下我都可以,例如,foo[1][2][3][4][5].add(1)

但我一直在寻找一种不那么繁琐的方法来实现这一点,并找到了两种方法。第一个也提供了与上述解决方案相同的位置:

class NestedDict(dict):
  def __getitem__(self, key):
    if key in self: return self.get(key)
    return self.setdefault(key, NestedDict())

以及在 SO 上找到的第二个等价物作为Autovivification 问题的答案。

class NestedDict(dict):
  """Implementation of perl's autovivification feature."""
  def __getitem__(self, item):
    try:
      print "__getitem__: %s" % item
      return dict.__getitem__(self, item)
    except KeyError:
      value = self[item] = type(self)()
      print "value: %s" % value
      return value

我喜欢最后两种方法,但我不知道如何更改它们以生成特定类型的嵌套字典,例如,不是 dictsetlist使用defaultdict.

提前感谢您的任何建议、评论或更正。

4

2 回答 2

2

这是一个自动激活器,它不需要您设置您想要默认工厂的级别。当您获得 DefaultHasher 上不存在的属性时,它会将自身更改为默认工厂的实例:

class DefaultHasher(dict):
    def __init__(self, default_factory, change_self=None):
        self.default_factory = default_factory
        self.change_self = change_self
    def change(self, key):
        def _change():
            x = self.default_factory()
            self[key] = x
            return x
        return _change
    def __missing__(self, key):
        self[key] = DefaultHasher(self.default_factory,
                                  self.change(key))
        return self[key]
    def __getattr__(self, name):
        result = self.change_self()
        return getattr(result, name)

foo = DefaultHasher(set)
foo[1][2][3][4][5].add(1)
print(foo)
# {1: {2: {3: {4: {5: set([1])}}}}}

foo[1][2][3].add(20)
print(foo)
# {1: {2: {3: set([20])}}}

foo[1][3] = foo[1][2]
print(foo)
# {1: {2: {3: set([20])}, 3: {3: set([20])}}}

foo[1][2].add(30)
print(foo)
# {1: {2: set([30]), 3: {3: set([20])}}}
于 2013-07-12T13:29:04.533 回答
1

这种方法类似于您的第一个示例,除了您可以指定所需的任何深度而无需大量输入。

from collections import defaultdict

def nested_default_dict(num_keys, init_func):
    if num_keys == 1:
        return defaultdict(init_func)
    else:
        return defaultdict(lambda: nested_default_dict(num_keys-1, init_func))

foo = nested_default_dict(5, set)
foo[1][2][3][4][5].add("Hello World")
foo[1][2][3][4][5].add("Lorem Ipsum")
foo[1][2][3][4][5].add("Dolor sit amet")
print foo[1][2][3][4][5]

bar = nested_default_dict(3, list)
bar[4][8][15].append(16)
bar[4][8][15].append(23)
bar[4][8][15].append(42)
print bar[4][8][15]

结果:

set(['Dolor sit amet', 'Lorem Ipsum', 'Hello World'])
[16, 23, 42]

一个缺点是当您打印时,dicts 看起来不是很漂亮:

>>>print foo
defaultdict(<function <lambda> at 0x0000000001FA8A58>, {1: defaultdict(<function <lambda> at 0x0000000001FAB198>, {2: defaultdict(<function <lambda> at 0x0000000001FAB208>, {3: defaultdict(<function <lambda> at 0x0000000001FAB278>, {4: defaultdict(<type 'set'>, {5: set(['Dolor sit amet', 'Lorem Ipsum', 'Hello World'])})})})})})
于 2013-07-12T13:05:28.387 回答