访问多级词典的最后一个值

时间:2019-05-16 16:09:36

标签: python dictionary

我有一个字符串输入,我将其转换为字典。通常,结果字典具有多个级别,即嵌套结构。

我想访问其“叶”元素,而与字典级别的数量无关。

示例:

# inputs arriving to my code from external users
input1 = "{'country':{'name':'Italy', 'region':'Europe'}}"
input2 = "{'country':{'first_country':{'name':'Italy', 'region':'Europe'}, 'second_country':{'name':'France', 'region':'Europe'}}}"

import ast
dict1 = ast.literal_eval(input1)
dict2 = ast.literal_eval(input2)

print(dict1)
>>> {'country': {'name': 'Italy', 'region': 'Europe'}}

print(dict2)
>>> {'country': {'first_country': {'name': 'Italy', 'region': 'Europe'}, 'second_country': {'name': 'France', 'region': 'Europe'}}}

是否有一种方法可以独立于传递的字典来访问字段nameregion

我无法预先知道字典级别的数量,因为它们取决于外部用户输入的输入。我想访问字典的最后一个级别,而与它的级别无关。

编辑

让代码说话。以下代码可以实现我想要的功能,但是我的大脑中的某些内容建议我避免使用exec语句。

#swap Italy country with Germany, independently from the passed dictionary
def levels(d):
    return max(count(v) if isinstance(v,dict) else 0 for v in d.values()) + 1

def swapCountry(inputDict):
    if levels(inputDict) == 2:
        path = "dict1['country']['name']"
    elif levels(inputDict) == 3:
        path = "dict2['country']['first_country']['name']"

    exec('{} = "Germany"'.format(path)) 

因此:

swapCountry(dict1)
print(dict1)
>>> {'country': {'name': 'Germany', 'region': 'Europe'}}

swapCountry(dict2)
print(dict2)
>>> {{'country': {'first_country': {'name': 'Germany', 'region': 'Europe'}, 'second_country': {'name': 'France', 'region': 'Europe'}}}

1 个答案:

答案 0 :(得分:1)

这是一种简单的方法:

import ast

def get_leafs(d):
    res = [leaf for v in d.values() if isinstance(v, dict) for leaf in get_leafs(v)]
    if not res:
        res = [d]
    return res

# inputs arriving to my code from external users
input1 = "{'country':{'name':'Italy', 'region':'Europe'}}"
input2 = "{'country':{'first_country':{'name':'Italy', 'region':'Europe'}, 'second_country':{'name':'France', 'region':'Europe'}}}"
dict1 = ast.literal_eval(input1)
dict2 = ast.literal_eval(input2)

print(get_leafs(dict1))
# [{'name': 'Italy', 'region': 'Europe'}]
print(get_leafs(dict2))
# [{'name': 'Italy', 'region': 'Europe'}, {'name': 'France', 'region': 'Europe'}]

如果您更喜欢使用发电机:

def get_leafs_gen(d):
    if any(isinstance(v, dict) for v in d.values()):
        yield from (leaf for v in d.values() if isinstance(v, dict) for leaf in get_leafs(v))
    else:
        yield d

print(*get_leafs_gen(dict1))
# {'name': 'Italy', 'region': 'Europe'}
print(*get_leafs_gen(dict2))
# {'name': 'Italy', 'region': 'Europe'} {'name': 'France', 'region': 'Europe'}

编辑:如果您希望拥有将您带到每一片叶子的钥匙,则可以使用以下内容:

def get_leaf_keys_gen(d):
    if any(isinstance(v, dict) for v in d.values()):
        for k, v in d.items():
            if isinstance(v, dict):
                for leaf_key in get_leaf_keys_gen(v):
                    yield (k,) + leaf_key
    else:
        yield ()

print(*get_leaf_keys_gen(dict1))
# ('country',)
print(*get_leaf_keys_gen(dict2))
# ('country', 'first_country') ('country', 'second_country')

然后实际获取叶子:

def get_from_multikey(d, key):
    if not key:
        return d
    else:
        return get_from_multikey(d[key[0]], key[1:])

print(*(get_from_multikey(dict1, key) for key in get_leaf_keys_gen(dict1)))
# {'name': 'Italy', 'region': 'Europe'}
print(*(get_from_multikey(dict2, key) for key in get_leaf_keys_gen(dict2)))
# {'name': 'Italy', 'region': 'Europe'} {'name': 'France', 'region': 'Europe'}

编辑2:添加了另一种替代方法,将访问者功能应用于每个叶子:

def visit_leaves(d, visitor):
    if any(isinstance(v, dict) for v in d.values()):
        for v in d.values():
            if isinstance(v, dict):
                visit_leaves(v, visitor)
    else:
        visitor(d)

# Adds a new item 'n' to every leaf in dict1
visit_leaves(dict1, lambda leaf: leaf.setdefault('n', 0))
print(dict1)
# {'country': {'name': 'Italy', 'region': 'Europe', 'n': 0}}

# Removes 'region' from every leaf in dict2
visit_leaves(dict2, lambda leaf: leaf.pop('region', None))
print(dict2)
# {'country': {'first_country': {'name': 'Italy'}, 'second_country': {'name': 'France'}}}