Python 存储字典路径并读取回来

5

我正在遍历一个嵌套非常深的列表字典(系统信息),并以以下格式存储键的完整路径:

.children[0].children[9].children[0].children[0].handle = PCI:0000:01:00.0
.children[0].children[9].children[0].children[0].description = Non-Volatile memory controller
.children[0].children[9].children[0].children[0].product = Samsung Electronics Co Ltd
.children[0].children[9].product = Xeon E7 v4/Xeon E5 v4/Xeon E3 v4/Xeon D DMI2
.children[2].product = PWS-406P-1R

接下来,完整路径被读入并将与系统信息(Data)进行比较。我该如何将完整路径转换为此格式?

Data['children'][0]['children'][9]['children'][0]['children'][0]['handle']
Data['children'][0]['children'][9]['product]'
Data['children'][2]['product']

我可以做一些类似于以下这样的事情:
data = re.findall(r"\.([a-z]+)\[(\d+)\]", key, re.IGNORECASE)

[('children', '0'), ('children', '9'), ('children', '0'), ('children', '0')]
[('children', '0'), ('children', '9'), ('children', '0'), ('children', '0')]
[('children', '0'), ('children', '9'), ('children', '0'), ('children', '0')]
[('children', '0'), ('children', '9')]
[('children', '2')]

我该如何将这些元组列表转换成可以进行以下操作的形式: ```python ```
if Data['children'][2]['product'] == expected:
    print('pass')

请确认一下 - 您的第一个样本是代表嵌套数据的文本文件吗? - Tomalak
是的,那是正确的。 - officespacejam
1
为什么不使用JSON?除非你有一个非常好的理由,否则不要自己编写文件格式。JSON对于你存储的内容来说是很好的选择,并且Python内置了读写JSON的工具。 - Tomalak
4个回答

1
你可以使用 itertoolsfunctoolsoperator 库将索引链接在一起,并递归地查找它们以获取最终值。
首先,我认为你应该更改正则表达式以选择最后一个 getter(即 handle, description, product)。
re.findall(r"\.([a-z]+)(?:\[(\d+)\])?", key, re.IGNORECASE)

那应该给你这个。
[('children', '0'), ('children', '9'), ('product', '')]

然后你可以像这样链接查找。
import operator
import functools
import itertools

indexes = [('children', '0'), ('children', '9'), ('product', '')]

# This turns the list above into a flat list ['children', 0, 'children', ...]
# It also converts number strings to integers and excludes empty strings.
keys = (int(k) if k.isdigit() else k for k in itertools.chain(*indexes) if k)

# functools.reduce recursively looks up the keys
# operator.getitem() is a functional version of Data[key] == getitem(Data, key)
value = functools.reduce(operator.getitem, keys, Data)
if value == expected:
    pass

0
我现在能想到的最简单的方法是:
代码:
s = '.children[2].product = PWS-406P-1R'
path, expected = re.sub(r'\.(\w+)', r"['\1']", s).split(' = ')
Data = {'children': ['', '', {'product': 'PWS-406P-1R'}]}

if eval(f'Data{path}') == expected:
    print('pass')

输出:

pass

注意使用 f-strings,需要 Python 3.6+。如果你喜欢的话,可以将其更改为 .format()


0

它可以在 Data 结构中使用递归搜索来工作:

s = """.children[0].children[9].children[0].children[0].handle = PCI:0000:01:00.0
.children[0].children[9].children[0].children[0].description = Non-Volatile memory controller
.children[0].children[9].children[0].children[0].product = Samsung Electronics Co Ltd
.children[0].children[9].product = Xeon E7 v4/Xeon E5 v4/Xeon E3 v4/Xeon D DMI2
.children[2].product = PWS-406P-1R
"""

import re

Data = {'children': {'0': {'children': {'9': {'children': {'0': {'children': {'0': {'handle': 'value'}}}}}}}}}

for line in s.splitlines():
    l, value = re.split(r'\s*=\s*', line)
    l = l[1:] # Remove first '.'
    keys = re.split(r'[\[\].]+', l)
    print(keys)

    lookup = Data
    for key in keys:
        if key in lookup:
            lookup = lookup[key]
        else:
            print("Key {} not found".format(key))
            raise Exception("Value not found for {}".format(".".join(keys)))

    print("Value found: " + value)

第一个拆分将键与数据分开(寻找[code]=[/code])

l = l[1:]移除了第一个“.”

第二个拆分将所有字段分离成密钥列表以访问数据。

然后,进行数据结构中的查找循环。


Data['children']Data['children'][n]['children']等都是列表,我猜测但在问题中没有定义^^。 - radzak

0

这里有另一个正则表达式尝试着去完成这个任务:

pattern = re.compile(r'(?:\.(\w+)(?:\[(\d+)\]))|(?:\.(\w+))|(?:\s*=\s*(.+)$)')

path = '.children[0].children[9].children[0].children[0].handle = PCI:0000:01:00.0'

这里就是魔力所在:
>>> map(lambda t: filter(None, t), pattern.findall(path))
[('children', '0'), ('children', '9'), ('children', '0'), ('children', '0'), ('handle',), ('PCI:0000:01:00.0',)]

进一步地,将结果列表压平

>>> import itertools
>>> keys = map(lambda t: filter(None, t), pattern.findall(path))
>>> flatkeys = list(itertools.chain.from_iterable(map(lambda key: (key[0], int(key[1])) if len(key) > 1 else (key[0],), keys[:-1])))
>>> flatkeys
['children', 0, 'children', 9, 'children', 0, 'children', 0, 'handle']
>>> result = keys[-1][0]
>>> result
'PCI:0000:01:00.0'

现在借鉴自这个答案

>>> d = dict(children=[dict(children=([{} for _ in range(9)]) + [dict(children=[dict(children=[dict(handle='PCI:0000:01:00.0')])])])])
>>> d
{'children': [{'children': [{}, {}, {}, {}, {}, {}, {}, {}, {}, {'children': [{'children': [{'handle': 'PCI:0000:01:00.0'}]}]}]}]}

>>> from functools import reduce
>>> import operator
>>> assert reduce(operator.getitem, flatkeys, d) == result

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接