Python中的多个"collection.defaultdict"级别

Mor*_*ock 169 python dictionary nested

感谢SO的一些优秀人员,我发现了collections.defaultdict可见性和速度提供的可能性.我已经把它们用于成功.

现在我想实现三个级别的词典,两个顶级词典defaultdict和最低词典int.我找不到合适的方法来做到这一点.这是我的尝试:

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]
Run Code Online (Sandbox Code Playgroud)

现在这可行,但以下,这是所需的行为,不会:

d["key4"]["a1"] + 1
Run Code Online (Sandbox Code Playgroud)

我怀疑我应该声明第二级defaultdict是类型的int,但我没有找到在哪里或如何这样做.

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

还有更优雅的建议吗?

谢谢pythoneers!

int*_*jay 326

使用:

from collections import defaultdict
d = defaultdict(lambda: defaultdict(int))
Run Code Online (Sandbox Code Playgroud)

defaultdict(int)每当访问新密钥时,这将创建一个新的d.

  • @Noah:如果使用命名的模块级函数而不是lambda,它将会发生腌制. (19认同)
  • 这太棒了.我似乎每天都将我的婚姻誓言更新为Python. (10认同)
  • @ScienceFriction您需要帮助的具体内容吗?当访问`d [new_key]`时,它将调用lambda,它将创建一个新的`defaultdict(int)`.当访问`d [existing_key] [new_key2]`时,将创建一个新的`int`. (4认同)
  • 通过`multiprocessing`查找有关使用此方法的更多详细信息以及命名的模块级函数是什么?这个[问题](http://stackoverflow.com/questions/16439301/cant-pickle-defaultdict)会跟进. (3认同)
  • 唯一的问题是它不会泡菜,这意味着“多处理”对于来回发送这些消息不满意。 (2认同)

Nat*_*ile 31

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

from functools import partial
...
d = defaultdict(partial(defaultdict, int))
Run Code Online (Sandbox Code Playgroud)

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

"你不能挑选一个部分对象,除非它包装的函数[或者在这种情况下,类]是全局可访问的......在它的__name__(在__module__中)" - Pickling 包含的部分函数


mil*_*s82 12

请看一下nosklo的答案,以获得更通用的解决方案.

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
Run Code Online (Sandbox Code Playgroud)

测试:

a = AutoVivification()

a[1][2][3] = 4
a[1][3][3] = 5
a[1][2]['test'] = 6

print a
Run Code Online (Sandbox Code Playgroud)

输出:

{1: {2: {'test': 6, 3: 4}, 3: {3: 5}}}
Run Code Online (Sandbox Code Playgroud)

  • 不幸的是,这种解决方案没有保留defaultdict最方便的部分,即可以写D ['key'] + = 1之类的东西而不必担心密钥的存在。这是我将defaultdict用于...的主要功能,但是我可以想象动态加深词典也非常方便。 (2认同)
  • @rschwieb可以通过添加__add__方法来增加写+ = 1的能力。 (2认同)

Rad*_*ing 6

聚会迟到了,但为了任意深度,我发现自己在做这样的事情:

from collections import defaultdict

class DeepDict(defaultdict):
    def __call__(self):
        return DeepDict(self.default_factory)
Run Code Online (Sandbox Code Playgroud)

这里的技巧基本上是使DeepDict实例本身成为构建缺失值的有效工厂。现在我们可以做这样的事情

dd = DeepDict(DeepDict(list))
dd[1][2].extend([3,4])
sum(dd[1][2])  # 7

ddd = DeepDict(DeepDict(DeepDict(list)))
ddd[1][2][3].extend([4,5])
sum(ddd[1][2][3])  # 9
Run Code Online (Sandbox Code Playgroud)


spa*_*azm 5

按照@rschwieb的要求D['key'] += 1,我们可以通过定义方法覆盖加法来扩展前一个__add__方法,以使其表现得更像collections.Counter()

首先__missing__将被调用以创建一个新的空值,该值将传递到中__add__。我们测试该值,以空值为False

有关覆盖的更多信息,请参见模拟数字类型

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
Run Code Online (Sandbox Code Playgroud)

例子:

>>> 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}}}
Run Code Online (Sandbox Code Playgroud)

我们可以只提供默认的0值,然后尝试执行该操作,而不是检查参数是否为Number(非常非python,无粉动物!)。

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
Run Code Online (Sandbox Code Playgroud)