内置的字典验证解决方案

3

我正在使用以下代码来验证一个字典(a)是否与另一个字典(check_against)相同。不幸的是,我的代码不太易读,所以我想知道是否有更快/更干净的内置解决方案来实现相同的结果。也许我只是没有搜索到正确的关键词,但我没有找到任何关于我认为是相当常见的任务的讨论。

check_against = {
    'a' : str,
    'b' : {
        'c': int,
        'd': int,
    }
}

a = {
   'a' : 1,
   'c' : 1
}

def get_type_at_path(obj, chain):
    _key = chain.pop(0)
    if _key in obj:
        return key_exists(obj[_key], chain) if chain else type(obj[_key])

def root_to_leaf_paths(tree, cur=()):
    if isinstance(tree,dict):
        for n, s in tree.items():
            for path in root_to_leaf_paths(s, cur+(n,)):
                yield path
    else:
        yield [cur,tree]

for path,value_type in root_to_leaf_paths(check_against):
    a_value_type = get_type_at_path(a,list(path))
    if a_value_type == None:
        print(f"Missing key at path \"{list(path)}\"")
    elif not a_value_type == value_type:
        print(f"Value at path \"{list(path)}\" should be of type \"{value_type}\" but got {a_value_type}")

输出
Value at path "['a']" should be of type "<class 'str'>" but got <class 'int'>
Missing key at path "['b', 'c']"
Missing key at path "['b', 'd']"

你的意思是要确定这些字典是否相等,还是只需要判断A是否包含了B中的所有元素(但也可能多出其他元素)?或者...?请明确目标。 - Arthur Dent
这不完全是您要求的,但jsonschema可能会有所帮助?https://pypi.org/project/jsonschema/ - James
1
你的代码能正常工作吗? - wwii
是的,它可以工作,只是不够优雅。我认为它也可以更快。我正在寻找一个内置的解决方案。 - TheAschr
5
投票关闭 - 该问题适合在Code Review上讨论。 - wwii
显示剩余4条评论
2个回答

2

您可以稍微调整您的 root_to_leaf_paths() 函数,将其视为通用字典扁平化程序。对架构和数据进行扁平化处理。然后比较就很简单了。

schema = {
    'a' : str,
    'b' : {
        'c': int,
        'd': int,
    }
}

data = {
   'a' : 1,
   'c' : 1
}

def flatten(obj, path = tuple()):
    if isinstance(obj, dict):
        for k, v in obj.items():
            yield from flatten(v, path + (k,))
    else:
        yield (path, obj)

fschema = dict(flatten(schema))
fdata = dict(flatten(data))

for path, exp in fschema.items():
    if path in fdata:
        got = type(fdata[path])
        if got is not exp:
            print(f'Incorrect type: path={path} got={got} exp={exp}')
    else:
        print(f'Missing key: path={path}')

1
你可以将check_against字典扁平化,只包含映射到类型的键,然后运行a
check_against = {'a': <class 'str'>, 'b': {'c': <class 'int'>, 'd': <class 'int'>}}
a = {'a': 1, 'c': 1}
def flatten(d):
  _v = [[(a, b)] if not isinstance(b, dict) else flatten(b) for a, b in d.items()]
  return [i for b in _v for i in b]

new_check = dict(flatten(check_against))
for c, d in a.items():
  if not isinstance(d, new_check[c]):
    raise TypeError("At key '{}': expecting value of type '{}', got '{}'".format(c, new_check[c].__name__, type(d).__name__))

当运行时,检查成功地针对不正确的类型引发错误:
TypeError: At key 'a': expecting value of type 'str', got 'int'

编辑:利用a作为检查字典:
def check_values(d, check_dict = a):
  for a, b in d.items():
     if a in check_dict and not isinstance(check_dict[a], b):
        raise TypeError("At key '{}': expecting type '{}' but got '{}'".format(a, type(check_dict[a]).__name__, b.__name__))
     if isinstance(b, dict):
       check_values(b)

输出:

TypeError: At key 'a': expecting type 'int' but got 'str'

我认为这个解决方案在我的情况下不起作用,因为我认为你的解决方案是相反的(将“a”与“check_against”进行比较)。这意味着当check_against为{'e': str, 'b': {'c': int, 'd': int}}时,它会导致关键错误。尽管仍在努力理解代码。 - TheAschr
实际上,我猜如果出现键错误,那就相当于我的“缺少路径X”的输出。 - TheAschr
@TheAschr 请查看我的最近编辑。 - Ajax1234

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