我对嵌套字典的看法是:
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'}},
这显然是不正确的。有什么问题?
答案 0 :(得分:2)
使用collections.defaultdict()
object自动实例化嵌套词典:
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
将为我们创建一个新字典。
答案 1 :(得分: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]
批量处理。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行之后到目前为止还没有打印出dict,但是通过将理解转换为显式循环(理想情况下使用enumerate
而不是保持显式{{1},这很容易添加计数器)。