我使用嵌套字典的方式是这样的:
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'}},
这显然是不正确的。怎么了?
使用 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
将为我们创建一个新字典。
虽然这不是做事的理想方式,但你已经非常接近让它发挥作用了。
您的主要问题是您正在重用相同的tmp
字典。将其插入dicty
的第一个键下后,clear
它并开始用新值填充它。将tmp.clear()
替换为 tmp = {}
来解决此问题,因此每个键都有不同的字典,而不是所有键都有相同的字典。
第二个问题是,当你到达最后时,你永远不会在字典中存储最后一个tmp
值,所以在for
循环之后再添加一个dicty[oldword] = tmp
。
你的第三个问题是你正在检查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
,然后将每个组转换为字典,并在一个步骤中插入所有内容。这与现有代码一样,要求输入已由values[0]
进行批处理。 - 将字典用作字典。您可以在每个键进来时查找它,如果找到,则添加到值中,如果没有,则创建一个新键。
defaultdict
或setdefault
方法将使它简洁,但即使你不知道这些,明确地写出来也很简单,而且它仍然不会像现在这样冗长。
第二个版本在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 行后打印出字典,但通过将理解转换为显式循环(理想情况下使用 enumerate
而不是保留显式row
计数器(很容易添加。