2017-08-22 69 views
6

Mam słownik z informacją config:Jak sprawdzić poprawność struktury (lub schematu) słownika w Pythonie?

my_conf = { 
    'version': 1, 

    'info': { 
     'conf_one': 2.5, 
     'conf_two': 'foo', 
     'conf_three': False, 
     'optional_conf': 'bar' 
    } 
} 

Chcę sprawdzić, czy słownika następujące struktury muszę.

szukam coś takiego:

conf_structure = { 
    'version': int, 

    'info': { 
     'conf_one': float, 
     'conf_two': str, 
     'conf_three': bool 
    } 
} 

is_ok = check_structure(conf_structure, my_conf) 

Czy istnieje rozwiązanie tego problemu zrobić lub dowolnej biblioteki, które mogłyby uczynić realizacji check_structure bardziej proste?

Odpowiedz

3

Bez użycia bibliotek, można również zdefiniować prostą funkcję rekurencyjną tak:

def check_structure(struct, conf): 
    if isinstance(struct, dict) and isinstance(conf, dict): 
     # struct is a dict of types or other dicts 
     return all(k in conf and check_structure(struct[k], conf[k]) for k in struct) 
    else: 
     # struct is the type of conf 
     return isinstance(conf, struct) 

ta zakłada, że ​​config może mieć klucze, które nie są w swojej strukturze, jak w przykładzie .

8

Można użyć schema (PyPi Link)

schematu jest biblioteką do sprawdzania poprawności struktury danych Python, takie jak te otrzymane z config-plików, formularzy, usług zewnętrznych lub wiersza poleceń parsowania, przeliczone z JSON/YAML (lub coś innego) do typów danych Pythona.

from schema import Schema, And, Use, Optional, SchemaError 

def check(conf_schema, conf): 
    try: 
     conf_schema.validate(conf) 
     return True 
    except SchemaError: 
     return False 

conf_schema = Schema({ 
    'version': And(Use(int)), 
    'info': { 
     'conf_one': And(Use(float)), 
     'conf_two': And(Use(str)), 
     'conf_three': And(Use(bool)), 
     Optional('optional_conf'): And(Use(str)) 
    } 
}) 

conf = { 
    'version': 1, 
    'info': { 
     'conf_one': 2.5, 
     'conf_two': 'foo', 
     'conf_three': False, 
     'optional_conf': 'bar' 
    } 
} 

print(check(conf_schema, conf)) 
+0

Wygląda świetnie! Dzięki :) –

+0

To jest kopiuj wklej od dokumentów. Jak dokładnie to pomogłoby OP? Czy możesz podać konkretny przykład pokazujący, w jaki sposób? To nie jest dużo lepsze niż odpowiedź typu "link-only" w obecnej formie. –

+0

@ cᴏʟᴅsᴘᴇᴇᴅ, ponieważ demonstruje wszystkie funkcje potrzebne OP i pokazuje, jak używać lambdas w zaawansowanych warunkach. Ale masz rację, dodam konkretny przykład do mojej odpowiedzi. –

0

@tobias_k pokonać mnie do niej (zarówno w czasie i jakości prawdopodobnie) ale tu jest inna funkcja rekurencyjna dla zadania, które mogą być nieco łatwiej (i ja) do naśladowania:

def check_dict(my_dict, check_against): 
    for k, v in check_against.items(): 
     if isinstance(v, dict): 
      return check_dict(my_dict[k], v) 
     else: 
      if not isinstance(my_dict[k], v): 
       return False 
    return True 
0

You można zbudować strukturę za pomocą rekurencji:

def get_type(value): 
    if isinstance(value, dict): 
     return {key: get_type(value[key]) for key in value} 
    else: 
     return str(type(value)) 

a następnie porównać z wymaganą strukturę słownika:

get_type(current_conf) == get_type(required_conf) 

przykład:

required_conf = { 
    'version': 1, 
    'info': { 
     'conf_one': 2.5, 
     'conf_two': 'foo', 
     'conf_three': False, 
     'optional_conf': 'bar' 
    } 
} 

get_type(required_conf) 

{'info': {'conf_two': "<type 'str'>", 'conf_one': "<type 'float'>", 'optional_conf': "<type 'str'>", 'conf_three': "<type 'bool'>"}, 'version': "<type 'int'>"} 
0

Charakter słowników, jeśli są one wykorzystywane w pytona nie eksportowane pewnym JSON jest to, że kolejność słownika nie musi być ustawiony. Zamiast tego, wyszukiwanie kluczy zwraca wartości (stąd słownik).

W obu przypadkach funkcje te powinny dostarczyć informacji na temat poziomu zagnieceń w dostarczonych próbkach.

#assuming identical order of keys is required 

def check_structure(conf_structure,my_conf): 
    if my_conf.keys() != conf_structure.keys(): 
     return False 

    for key in my_conf.keys(): 
     if type(my_conf[key]) == dict: 
      if my_conf[key].keys() != conf_structure[key].keys(): 
       return False 

    return True 

#assuming identical order of keys is not required 

def check_structure(conf_structure,my_conf): 
    if sorted(my_conf.keys()) != sorted(conf_structure.keys()): 
     return False 

    for key in my_conf.keys(): 
     if type(my_conf[key]) != dict: 
      return False 
     else: 
      if sorted(my_conf[key].keys()) != sorted(conf_structure[key].keys()): 
       return False 

    return True 

Rozwiązanie to oczywiście należy zmienić, jeśli poziom zagnieżdżenia była większa (czyli jest on skonfigurowany do oceny podobieństwa w strukturze słowników, które mają pewne wartości jak słowniki, ale nie słowniki gdzie niektóre wartości tych ostatnich słowniki są również słownikami).

Powiązane problemy