Désolé si cette question a déjà reçu une réponse - je cherchais des solutions, mais je n'utilise peut-être pas les termes de recherche corrects.
Quoi qu'il en soit, ce que j'essaie de faire est de définir par programme une valeur dans un dictionnaire, potentiellement imbriquée, à partir d'une liste d'index et d'une valeur.
Ainsi, par exemple, supposons que ma liste d’indices soit:
['person', 'address', 'city']
et la valeur est
'New York'
Je veux par conséquent un objet dictionnaire comme:
{ 'Person': { 'address': { 'city': 'New York' } }
Fondamentalement, la liste représente un "chemin" dans un dictionnaire imbriqué.
Je pense que je peux construire le dictionnaire lui-même, mais ce qui me dérange, c'est comment définir la valeur. De toute évidence, si j’écrivais simplement du code pour cela manuellement, ce serait:
dict['Person']['address']['city'] = 'New York'
Mais comment puis-je indexer dans le dictionnaire et définir la valeur comme celle-ci par programme si je n'ai qu'une liste des index et la valeur?
J'espère que cela a du sens et n'est pas une question trop bête ... :) Merci pour toute aide.
Quelque chose comme ça pourrait aider:
def nested_set(dic, keys, value):
for key in keys[:-1]:
dic = dic.setdefault(key, {})
dic[keys[-1]] = value
Et vous pouvez l'utiliser comme ceci:
>>> d = {}
>>> nested_set(d, ['person', 'address', 'city'], 'New York')
>>> d
{'person': {'address': {'city': 'New York'}}}
Tout d’abord, vous voudrez probablement regarder setdefault
En guise de fonction je l'écrirais comme
def get_leaf_dict( dict, key_list):
res=dict
for key in key_list:
res=dict.setdefault( key, {} )
return res
Ceci serait utilisé comme:
get_leaf_dict( dict, ['Person', 'address', 'city']) = 'New York'
Cela pourrait être nettoyé avec la gestion des erreurs et autres, en utilisant également *args
plutôt qu'un seul argument de liste de clés pourrait être Nice; mais l'idée est que vous pouvez parcourir les clés, en tirant le dictionnaire approprié à chaque niveau.
Voici ma solution simple: il suffit d'écrire
terms = ['person', 'address', 'city']
result = nested_dict(3, str)
result[terms] = 'New York' # as easy as it can be
Vous pouvez même faire:
terms = ['John', 'Tinkoff', '1094535332'] # account in Tinkoff Bank
result = nested_dict(3, float)
result[terms] += 2375.30
Maintenant les coulisses:
from collections import defaultdict
class nesteddict(defaultdict):
def __getitem__(self, key):
if isinstance(key, list):
d = self
for i in key:
d = defaultdict.__getitem__(d, i)
return d
else:
return defaultdict.__getitem__(self, key)
def __setitem__(self, key, value):
if isinstance(key, list):
d = self[key[:-1]]
defaultdict.__setitem__(d, key[-1], value)
else:
defaultdict.__setitem__(self, key, value)
def nested_dict(n, type):
if n == 1:
return nesteddict(type)
else:
return nesteddict(lambda: nested_dict(n-1, type))
Voici une autre option:
from collections import defaultdict
recursivedict = lambda: defaultdict(recursivedict)
mydict = recursivedict()
Je l’ai initialement obtenu à partir d’ici: https://stackoverflow.com/a/10218517/1530754 .
Assez intelligent et élégant si vous me demandez.
J'ai pris la liberté d'étendre le code de la réponse de Bakuriu . Par conséquent, les votes positifs à ce sujet sont facultatifs, car son code est en soi une solution spirituelle, à laquelle je n'aurais pas pensé.
def nested_set(dic, keys, value, create_missing=True):
d = dic
for key in keys[:-1]:
if key in d:
d = d[key]
Elif create_missing:
d = d.setdefault(key, {})
else:
return dic
if keys[-1] in d or create_missing:
d[keys[-1]] = value
return dic
Lorsque vous définissez create_missing
sur True, vous vous assurez de ne définir que les valeurs existantes:
# Trying to set a value of a nonexistent key DOES NOT create a new value
print(nested_set({"A": {"B": 1}}, ["A", "8"], 2, False))
>>> {'A': {'B': 1}}
# Trying to set a value of an existent key DOES create a new value
print(nested_set({"A": {"B": 1}}, ["A", "8"], 2, True))
>>> {'A': {'B': 1, '8': 2}}
# Set the value of an existing key
print(nested_set({"A": {"B": 1}}, ["A", "B"], 2))
>>> {'A': {'B': 2}}
Utilisez cette paire de méthodes
def gattr(d, *attrs):
"""
This method receives a dict and list of attributes to return the innermost value of the give dict
"""
try:
for at in attrs:
d = d[at]
return d
except:
return None
def sattr(d, *attrs):
"""
Adds "val" to dict in the hierarchy mentioned via *attrs
For ex:
sattr(animals, "cat", "leg","fingers", 4) is equivalent to animals["cat"]["leg"]["fingers"]=4
This method creates necessary objects until it reaches the final depth
This behaviour is also known as autovivification and plenty of implementation are around
This implementation addresses the corner case of replacing existing primitives
https://Gist.github.com/hrldcpr/2012250#gistcomment-1779319
"""
for attr in attrs[:-2]:
# If such key is not found or the value is primitive supply an empty dict
if d.get(attr) is None or isinstance(d.get(attr), dict):
d[attr] = {}
d = d[attr]
d[attrs[-2]] = attrs[-1]
Voici une variante de la réponse de Bakuriu qui ne repose pas sur une fonction distincte:
keys = ['Person', 'address', 'city']
value = 'New York'
nested_dict = {}
# Build nested dictionary up until 2nd to last key
# (Effectively nested_dict['Person']['address'] = {})
sub_dict = nested_dict
for key_ind, key in enumerate(keys[:-1]):
if not key_ind:
# Point to newly added piece of dictionary
sub_dict = nested_dict.setdefault(key, {})
else:
# Point to newly added piece of sub-dictionary
# that is also added to original dictionary
sub_dict = sub_dict.setdefault(key, {})
# Add value to last key of nested structure of keys
# (Effectively nested_dict['Person']['address']['city'] = value)
sub_dict[keys[-1]] = value
print(nested_dict)
>>> {'Person': {'address': {'city': 'New York'}}}