web-dev-qa-db-fra.com

Imprimer des clés et des valeurs spécifiques à partir d'un dictionnaire imbriqué profond dans Python 3.X

Je suis nouveau sur Python et j'ai essayé de chercher, mais je peux sembler trouver un échantillon de ce que j'essaie d'accomplir. Toutes les idées sont très appréciées. Je travaille avec un dictionnaire imbriqué avec beaucoup de clés et de valeurs, mais je souhaite uniquement imprimer celles spécifiques à l'aide d'une variable de liste filtrée.

my_nested_dict = {"global": {"peers": {"15.1.1.1": {"remote_id": "15.1.1.1", "address_family": {"ipv4": {"sent_prefixes": 1, "received_prefixes": 4, "accepted_prefixes": 4}}, "remote_as": 65002, "uptime": 13002, "is_enabled": true, "is_up": true, "description": "== R3 BGP Neighbor ==", "local_as": 65002}}, "router_id": "15.1.1.2"}}

Je voudrais filtrer et choisir les clés et les valeurs à imprimer

filtered_list = ['peers', 'remote_id', 'remote_as', 'uptime']

et réaliser un sur 

peers: 15.1.1.1
remote_id: 15.1.1.1
remote_as: 65002
uptime: 13002
9
JHCTac

Utilisez récursivité et isinstance:

my_nested_dict = {"global": {"peers": {"15.1.1.1": {"remote_id": "15.1.1.1", "address_family": {"ipv4": {"sent_prefixes": 1, "received_prefixes": 4, "accepted_prefixes": 4}}, "remote_as": 65002, "uptime": 13002, "is_enabled": True, "is_up": True, "description": "== R3 BGP Neighbor ==", "local_as": 65002}}, "router_id": "15.1.1.2"}}

filtered_list = ['peers', 'remote_id', 'remote_as', 'uptime']

def seek_keys(d, key_list):
    for k, v in d.items():
        if k in key_list:
            if isinstance(v, dict):
                print(k + ": " + list(v.keys())[0])
            else:
                print(k + ": " + str(v))
        if isinstance(v, dict):
            seek_keys(v, key_list)

seek_keys(my_nested_dict, filtered_list)

Remarque: Il y a une hypothèse intrinsèque ici que si vous voulez un jour la "valeur" d'une clé dont la valeur est un autre dictionnaire, vous obtenez la clé en premier .

13
JacobIRR

En plus de la réponse de @JacobIRR; je dirais que vous pouvez essayer de mettre en cache les données récurrentes dans un dict plat. De cette façon, ce sera beaucoup plus rapide que de récidiver à chaque fois. Ne vous inquiétez pas pour la mémoire car les valeurs du dict plat ne feront que se référer aux objets originaux du dictionnaire profond. Je vous laisse la modification du code de JacobIRR :).

0
Karthikeyan

@JacobIRR a posté une excellente réponse, mais puisque vous essayez de joindre les clés correspondantes à la valeur correspondante, une solution beaucoup plus courte apparaît:

my_nested_dict = {"global": {"peers": {"15.1.1.1": {"remote_id": "15.1.1.1", "address_family": {"ipv4": {"sent_prefixes": 1, "received_prefixes": 4, "accepted_prefixes": 4}}, "remote_as": 65002, "uptime": 13002, "is_enabled": True, "is_up": True, "description": "== R3 BGP Neighbor ==", "local_as": 65002}}, "router_id": "15.1.1.2"}}
_list = ['peers', 'remote_id', 'remote_as', 'uptime']

def _join(a, b):
  return '{}:{}\n'.format(a, _keys(b, True) if isinstance(b, dict) else b)

def _keys(_d, flag = False):
  return ''.join(_join(a, b) if a in _list else (a+'\n' if flag else '')+_keys(b) 
          for a, b in _d.items())

print(get_keys(my_nested_dict))

Sortie:

peers:15.1.1.1
remote_id:15.1.1.1
remote_as:65002
uptime:13002
0
Ajax1234

En Python 3, il est plus efficace et plus simple d’utiliser des générateurs au lieu de la récursivité:

from typing import Any, Tuple, Generator, FrozenSet

def search_in_dict(d: Any, keys: FrozenSet[str]) -> Generator[Tuple[str, Any], None, None]:
    """
    Generate pairs key-value for found keys
    """
    if not isinstance(d, dict):
        return
    for key, value in d.items():
        if key in keys:
            if isinstance(value, dict):
                # Special case: return the first key from nested dict as value
                yield key, Tuple(value.keys())[0]
            else:
                yield key, value
        # continue to search deeper
        yield from search_in_dict(value, keys)

puis pliez-le pour dicter à nouveau:

flatten_dict_with_results = dict(kv for kv in search_in_dict(my_nested_dict, keys=frozenset(filtered_list)))
0
denex

Vous pouvez utiliser jsonparse_ng (pip install jsonparse-ng). Sans la valeur if/else, la clé "pairs" est imprimée telle quelle (un dict). 

from jsonpath_ng.ext import parse

my_nested_dict = {
    "global": {
        "peers": {
            "15.1.1.1": {
                "address_family": {
                    "ipv4": {
                        "accepted_prefixes": 4,
                        "received_prefixes": 4,
                        "sent_prefixes": 1
                    }
                },
                "description": "== R3 BGP Neighbor ==",
                "is_enabled": true,
                "is_up": true,
                "local_as": 65002,
                "remote_as": 65002,
                "remote_id": "15.1.1.1",
                "uptime": 13002
            }
        },
        "router_id": "15.1.1.2"
    }
}
filtered_list = ['peers', 'remote_id', 'remote_as', 'uptime']

for list_item in filtered_list:
found = parse(f'$..{list_item}').find(my_nested_dict)
if found:
    if list_item == 'peers':
        print(f'{list_item} - {"".join(found[0].value.keys())}')
    else:
        print(f'{list_item} - {found[0].value}')
0
Frans