Python中多个级别的“collection.defaultdict”

感谢一些伟大的人们,我发现了collections.defaultdict提供的可能性,特别是在可读性和速度方面。 我已经把它们用于成功。

现在我想实现三个级别的字典,其中两个字典是defaultdict ,最低的是int 。 我没有find适当的方法来做到这一点。 这是我的尝试:

 from collections import defaultdict d = defaultdict(defaultdict) a = [("key1", {"a1":22, "a2":33}), ("key2", {"a1":32, "a2":55}), ("key3", {"a1":43, "a2":44})] for i in a: d[i[0]] = i[1] 

现在这是有效的,但是下面这个是所期望的行为,并不是:

 d["key4"]["a1"] + 1 

我怀疑我应该在某个地方声明第二层defaultdictinttypes的,但我没有find在哪里或如何做。

我首先使用defaultdict的原因是为了避免为每个新密钥初始化字典。

更优雅的build议?

感谢pythoneers!

使用:

 d = defaultdict(lambda: defaultdict(int)) 

这将在d访问新密钥时创build一个新的defaultdict(int)

另一种制作pickleable嵌套的defaultdict的方法是使用partial对象而不是lambda:

 from functools import partial ... d = defaultdict(partial(defaultdict, int)) 

这将起作用,因为defaultdict类可以在模块级全局访问:

“你不能腌一个部分对象,除非函数[或在这种情况下,类]包装是全局可访问…在__name__下(在__module__中)” – 腌制包装的部分函数

在这里看看nosklo的答案是一个更一般的解决scheme。

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

testing:

 a = AutoVivification() a[1][2][3] = 4 a[1][3][3] = 5 a[1][2]['test'] = 6 print a 

输出:

 {1: {2: {'test': 6, 3: 4}, 3: {3: 5}}} 

根据@ rschwieb对D['key'] += 1的请求,我们可以通过定义__add__方法来覆盖以前的扩展,使其更像一个collections.Counter()

首先__missing__来创build一个新的空值,并将其传递给__add__ 。 我们testing这个值,计算空值为False

有关覆盖的更多信息,请参阅模拟数字types 。

 from numbers import Number class autovivify(dict): def __missing__(self, key): value = self[key] = type(self)() return value def __add__(self, x): """ override addition for numeric types when self is empty """ if not self and isinstance(x, Number): return x raise ValueError def __sub__(self, x): if not self and isinstance(x, Number): return -1 * x raise ValueError 

例子:

 >>> import autovivify >>> a = autovivify.autovivify() >>> a {} >>> a[2] {} >>> a {2: {}} >>> a[4] += 1 >>> a[5][3][2] -= 1 >>> a {2: {}, 4: 1, 5: {3: {2: -1}}} 

而不是检查参数是一个数字(非非python,amirite!),我们可以提供一个默认的0值,然后尝试操作:

 class av2(dict): def __missing__(self, key): value = self[key] = type(self)() return value def __add__(self, x): """ override addition when self is empty """ if not self: return 0 + x raise ValueError def __sub__(self, x): """ override subtraction when self is empty """ if not self: return 0 - x raise ValueError