我有一个字符串输入,我将其转换为字典。通常,结果字典具有多个级别,即嵌套结构。
我想访问其“叶”元素,而与字典级别的数量无关。
示例:
# 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'}}}
是否有一种方法可以独立于传递的字典来访问字段name
和region
?
我无法预先知道字典级别的数量,因为它们取决于外部用户输入的输入。我想访问字典的最后一个级别,而与它的级别无关。
编辑
让代码说话。以下代码可以实现我想要的功能,但是我的大脑中的某些内容建议我避免使用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'}}}
答案 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'}}}