1

我使用嵌套字典的方式是这样的:

dicty = dict()
tmp = dict()
tmp["a"] = 1
tmp["b"] = 2
dicty["A"] = tmp

dicty == {"A" : {"a" : 1, "b" : 1}}

当我尝试在一个大文件上实现这一点时,问题就开始了,逐行读取。这是打印列表中每行的内容:

['proA', 'macbook', '0.666667']
['proA', 'smart', '0.666667']
['proA', 'ssd', '0.666667']
['FrontPage', 'frontpage', '0.710145']
['FrontPage', 'troubleshooting', '0.971014']

我想得到一个嵌套字典(忽略小数):

{'FrontPage': {'frontpage': '0.710145', 'troubleshooting': '0.971014'},
 'proA': {'macbook': '0.666667', 'smart': '0.666667', 'ssd': '0.666667'}}

当我逐行阅读时,我必须检查文件中是否仍然找到第一个单词(它们都已分组),然后再将其作为完整的字典添加到更高的字典中。

这是我的实现:

def doubleDict(filename):
    dicty = dict()
    with open(filename, "r") as f:
        row = 0
        tmp = dict()
        oldword = ""
        for line in f:
            values = line.rstrip().split(" ")
            print(values)
            if oldword == values[0]:
                tmp[values[1]] = values[2]
            else:
                if oldword is not "":
                    dicty[oldword] = tmp
                tmp.clear()
                oldword = values[0]
                tmp[values[1]] = values[2]
            row += 1
            if row % 25 == 0:
                print(dicty)
                break #print(row)
    return(dicty)

我实际上想在熊猫中使用它,但现在如果它可以用作字典,我会很高兴。出于某种原因,在阅读了前 5 行之后,我最终得到:

{'proA': {'frontpage': '0.710145', 'troubleshooting': '0.971014'}},

这显然是不正确的。怎么了?

4

2 回答 2

2

使用collections.defaultdict()对象自动实例化嵌套字典:

from collections import defaultdict

def doubleDict(filename):
    dicty = defaultdict(dict)
    with open(filename, "r") as f:
        for i, line in enumerate(f):
            outer, inner, value = line.split()
            dicty[outer][inner] = value
            if i % 25 == 0:
                print(dicty)
                break #print(row)
    return(dicty)

我曾经enumerate()在这里生成行数;比保持一个单独的柜台运行要简单得多。

即使没有defaultdict,您也可以让外部字典保留对嵌套字典的引用,并使用values[0];再次检索它 无需保留temp参考:

>>> dicty = {}
>>> dicty['A'] = {}
>>> dicty['A']['a'] = 1
>>> dicty['A']['b'] = 2
>>> dicty
{'A': {'a': 1, 'b': 1}}

然后所做的defaultdict就是让我们不必测试我们是否已经创建了那个嵌套字典。代替:

if outer not in dicty:
    dicty[outer] = {}
dicty[outer][inner] = value

我们只是省略了if测试,defaultdict如果键还不存在,将为我们创建一个新字典。

于 2013-10-05T23:16:28.783 回答
1

虽然这不是做事的理想方式,但您已经非常接近让它发挥作用了。

您的主要问题是您正在重用同一个tmp字典。将其插入到dicty第一个键下后,然后clear开始使用新值填充它。替换tmp.clear()tmp = {}来解决这个问题,所以每个键都有一个不同的字典,而不是所有键都用同一个字典。

您的第二个问题是,tmp当您到达末尾时,您永远不会将最后一个值存储在字典中,因此在循环dicty[oldword] = tmp之后添加另一个值。for

您的第三个问题是您正在检查if oldword is not "":. 即使它是一个空字符串,这也可能是正确的,因为您正在比较身份,而不是平等。只需将其更改为if oldword:. (这个,你通常会侥幸逃脱,因为小字符串通常会被拘留并且通常会共享身份......但你不应该指望这一点。)

如果你修复这两个,你会得到这个:

{'FrontPage': {'frontpage': '0.710145', 'troubleshooting': '0.971014'},
 'proA': {'macbook': '0.666667', 'smart': '0.666667', 'ssd': '0.666667'}}

我不确定如何将其转换为您声称想要的格式,因为该格式甚至不是有效的字典。但希望这能让你接近。


有两种更简单的方法可以做到这一点:

  • 使用例如 对值进行itertools.groupby分组,然后将每个组转换为 dict 并在一个步骤中将其全部插入。这与您现有的代码一样,要求输入已经被批处理values[0]
  • 将字典用作字典。您可以在每个键进入时查找它,如果找到则添加到值中,如果没有则创建一个新键。A defaultdictorsetdefault方法将使这个简洁,但即使你不知道这些,明确地写出来也很简单,而且它仍然比你现在拥有的更简洁。

Martijn Pieters 的回答已经很好地解释了第二个版本。

第一个可以这样写:

def doubleDict(s):
    with open(filename, "r") as f:
        rows = (line.rstrip().split(" ") for line in f)
        return {k: {values[1]: values[2] for values in g}
                for k, g in itertools.groupby(rows, key=operator.itemgetter(0))}

当然,在每 25 行之后到目前为止还没有打印出 dict,但是通过将理解转换为显式循环(理想情况下使用enumerate而不是保持显式row计数器)很容易添加。

于 2013-10-05T23:22:56.077 回答