Je passe en boucle sur un dictionnaire de listes fortement imbriqué (informations système) et stocke le chemin d'accès complet aux clés dans ce format:

.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

Ensuite, les chemins complets sont lus et seront comparés aux informations système (Données). Comment puis-je convertir le chemin complet dans ce format?

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

Je peux faire quelque chose comme:

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')]

Comment puis-je convertir une de ces listes de tuples pour pouvoir faire:

if Data['children'][2]['product'] == expected:
    print('pass')
5
officespacejam 11 avril 2018 à 21:53

4 réponses

Meilleure réponse

Vous pouvez utiliser les bibliothèques itertools, functools et operator pour enchaîner les index et les rechercher récursivement pour obtenir la valeur finale.

Tout d'abord, je pense que vous devriez changer l'expression régulière pour récupérer le dernier getter (c'est-à-dire handle, description, product)

re.findall(r"\.([a-z]+)(?:\[(\d+)\])?", key, re.IGNORECASE)

Cela devrait vous donner ceci

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

Ensuite, vous pouvez faire quelque chose comme ça pour enchaîner les recherches

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
1
Brendan Abel 11 avril 2018 à 20:15

La chose la plus simple à laquelle je puisse penser en ce moment est:

Code:

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')

Sortie:

pass

Notez l'utilisation de f-strings, nécessite Python 3.6+. Vous pouvez le changer en .format() si vous comme.

0
Jatimir 11 avril 2018 à 19:41

Cela pourrait fonctionner avec une recherche récursive dans la structure 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)

Le premier fractionnement sépare les clés des données (recherche de =)

l = l[1:] supprime le premier "."

Le deuxième fractionnement sépare tous les champs d'une liste de clés pour accéder aux données.

Ensuite, il y a une boucle de recherches dans la structure de données.

0
Dric512 11 avril 2018 à 19:43

Voici une autre expression rationnelle pour tenter de le faire:

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

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

Et voici la magie:

>>> map(lambda t: filter(None, t), pattern.findall(path))
[('children', '0'), ('children', '9'), ('children', '0'), ('children', '0'), ('handle',), ('PCI:0000:01:00.0',)]

Allez plus loin et aplatissez la liste résultante

>>> 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'

Maintenant, empruntez cette réponse

>>> 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
0
smac89 11 avril 2018 à 20:39