这是我的答案的更新版本,其中树数据结构的叶子现在与其余部分不同。树不再是严格意义上的 dict-of-nested-dicts,而是每个分支上的“叶子”现在是 dict 的不同子类的实例,名为 collections.Counter,这对于计算每个键出现的次数。我这样做是因为您回答了我的问题,即如果每行的最后一部分不是 ":pass"(即“我们必须为该键添加新计数”)会发生什么。
嵌套字典通常称为 Tree 数据结构,可以递归定义——根是字典,分支也是。以下使用dict 子类而不是普通的dict,因为它使构造它们更容易,因为您不需要特殊情况下创建下一级的第一个分支(除非我在添加“叶子”时仍然这样做" 因为它们是不同的子类,collections.Counter)。
from collections import Counter
from functools import reduce
import re
# (Optional) trick to make Counter subclass print like a regular dict.
class Counter(Counter):
def __repr__(self):
return dict(self).__repr__()
# Borrowed from answer @ https://stackoverflow.com/a/19829714/355230
class Tree(dict):
def __missing__(self, key):
value = self[key] = type(self)()
return value
# Utility functions based on answer @ https://stackoverflow.com/a/14692747/355230
def nested_dict_get(nested_dict, keys):
return reduce(lambda d, k: d[k], keys, nested_dict)
def nested_dict_set(nested_dict, keys, value):
nested_dict_get(nested_dict, keys[:-1])[keys[-1]] = value
def nested_dict_update_count(nested_dict, keys):
counter = nested_dict_get(nested_dict, keys[:-1])
if counter: # Update existing Counter.
counter.update([keys[-1]])
else: # Create a new Counter.
nested_dict_set(nested_dict, keys[:-1], Counter([keys[-1]]))
d = Tree()
pat = re.compile(r'[a-zA-z]+')
with open('abc.txt') as file:
for line in file:
nested_dict_update_count(d, [w for w in pat.findall(line.rstrip())])
print(d) # Prints like a regular dict.
为了测试修改后代码的叶子计数能力,我使用了以下测试文件,其中两次包含同一行,一次以:pass 结尾,另一次以:fail 结尾。
扩展abc.txt测试文件:
abc/pqr/lmn/xyz:pass
abc/pqr/lmn/bcd:pass
abc/pqr/lmn/xyz:fail
abc/pqr/lmn/xyz:pass
输出:
{'abc': {'pqr': {'lmn': {'bcd': {'pass': 1}, 'xyz': {'fail': 1, 'pass': 2}}}}}