6

我的代码目前必须计算一个严重嵌套dict到另一个中的东西。我有需要按 3 个值索引然后计数的项目。所以,在我的循环之前,我defaultdict像这样初始化一个嵌套:

from collections import defaultdict

type_to_count_dic = defaultdict(
        lambda: defaultdict(
            lambda: defaultdict(int)
        )
    )

这让我可以像这样在一个紧密的循环中计算项目:

for a in ...:
    for b in ...:
        for c in ...:
            type_to_count_dic[a][b][c] += 1

我觉得初始化所有这些defaultdicts 感觉很像在 Java 之类的东西中进行类型声明。有没有更惯用/ Pythonic的方式来做这样的事情?

4

4 回答 4

8
from collections import defaultdict

class _defaultdict(defaultdict):
    def __add__(self, other):
        return other

def CountTree():
    return _defaultdict(CountTree)

>>> t = CountTree()
>>> t['a']
defaultdict(<function CountTree at 0x9e5c3ac>, {})
>>> t['a']['b']['c'] += 1
>>> print t['a']['b']['c']
1
于 2013-05-05T12:14:56.730 回答
3

由于您正在计算事物,因此您应该使用 Counter 作为最内层的字典:

import collections
defaultdict = collections.defaultdict
Counter = collections.Counter

x = defaultdict(lambda: defaultdict(Counter))

for a in A:
    for b in B:
        x[a][b].update(C)

使用 Counter 将使您能够访问有用的方法,例如most_common

根据您打算使用此 dict 执行的操作,您可能不需要深度嵌套。相反,您可以使用元组作为键。例如,

import collections
import itertools as IT

A = range(2)
B = 'XYZ'
C = 'abc'
x = collections.Counter(IT.product(A, B, C))
print(x)

产量

A = range(2)
B = 'XYZ'
C = 'abc'
x = collections.Counter(IT.product(A, B, C))
print(x)

产量

Counter({(0, 'X', 'c'): 1, (0, 'Z', 'a'): 1, (1, 'Z', 'a'): 1, (1, 'X', 'c'): 1, (1, 'Z', 'b'): 1, (0, 'X', 'b'): 1, (0, 'Y', 'a'): 1, (1, 'Y', 'a'): 1, (0, 'Z', 'c'): 1, (1, 'Z', 'c'): 1, (0, 'X', 'a'): 1, (0, 'Y', 'b'): 1, (1, 'X', 'a'): 1, (1, 'Y', 'b'): 1, (0, 'Z', 'b'): 1, (1, 'Y', 'c'): 1, (1, 'X', 'b'): 1, (0, 'Y', 'c'): 1})
于 2013-05-05T11:56:15.450 回答
2

我假设您仅在满足某些条件时才添加到每个计数器,或者可能根据条件添加不同的值?否则肯定每个计数器的值总是1?

也就是说,我能想到的最简单的解决方案是只创建一个以三个循环值的元组为键的 dict。例如这样的:

dict(((a,b,c),1) for a in A for b in B for c in C)

但正如我所说,这只会在每个计数器中为您提供 1。您需要将上面表达式中的1替换为一些条件或函数调用,根据abc的值返回更合适的值。

于 2013-05-05T12:18:05.050 回答
0

我有类似的需求,并创建了以下内容:

import json

from collections import defaultdict


class NestedDefaultDict(defaultdict):
    def __init__(self, depth, default=int, _root=True):
        self.root = _root
        self.depth = depth
        if depth > 1:
            cur_default = lambda: NestedDefaultDict(depth - 1,
                                                    default,
                                                    False)
        else:
            cur_default = default
        defaultdict.__init__(self, cur_default)

    def __repr__(self):
        if self.root:
            return "NestedDefaultDict(%d): {%s}" % (self.depth,
                                                    defaultdict.__repr__(self))
        else:
            return defaultdict.__repr__(self)


# Quick Example
core_data_type = lambda: [0] * 10
test = NestedDefaultDict(3, core_data_type)
test['hello']['world']['example'][5] += 100
print test
print json.dumps(test)

# Code without custom class.
test = defaultdict(lambda: defaultdict(lambda: defaultdict(core_data_type)))
test['hello']['world']['example'][5] += 100
print test
print json.dumps(test)

如果我最终更新它,我还创建了一个要点:https ://gist.github.com/KyleJamesWalker/8573350

于 2014-01-23T15:08:07.960 回答