it-swarm.com.de

Festlegen eines Werts in einem verschachtelten Python-Wörterbuch anhand einer Liste von Indizes und Werten

Es tut uns leid, wenn diese Frage bereits beantwortet wurde. Ich habe nach Lösungen gesucht, verwende jedoch möglicherweise nicht die richtigen Suchbegriffe.

Ich versuche jedenfalls, programmgesteuert einen Wert in einem Wörterbuch festzulegen, der möglicherweise verschachtelt ist, wenn eine Liste von Indizes und ein Wert vorliegen.

Nehmen wir zum Beispiel an, meine Liste der Indizes ist:

['person', 'address', 'city'] 

und der Wert ist

'New York'

Ich möchte als Ergebnis ein Dictionary-Objekt wie:

{ 'Person': { 'address': { 'city': 'New York' } }

Grundsätzlich repräsentiert die Liste einen 'Pfad' in ein verschachteltes Wörterbuch.

Ich denke, ich kann das Wörterbuch selbst erstellen, aber ich stolpere darüber, wie ich den Wert festlegen kann. Offensichtlich wäre es, wenn ich nur manuell Code dafür schreiben würde:

dict['Person']['address']['city'] = 'New York'

Aber wie indiziere ich in das Wörterbuch und setze den Wert so programmgesteuert, wenn ich nur eine Liste der Indizes und des Werts habe?

Hoffe das macht Sinn und ist keine allzu blöde Frage ... :) Danke für jede Hilfe.

26
peterk

So etwas könnte helfen:

def nested_set(dic, keys, value):
    for key in keys[:-1]:
        dic = dic.setdefault(key, {})
    dic[keys[-1]] = value

Und du kannst es so benutzen:

>>> d = {}
>>> nested_set(d, ['person', 'address', 'city'], 'New York')
>>> d
{'person': {'address': {'city': 'New York'}}}
40
Bakuriu

Zunächst möchten Sie sich wahrscheinlich setdefault ansehen.

Als Funktion würde ich es schreiben als

def get_leaf_dict( dict, key_list):
    res=dict
    for key in key_list:
        res=dict.setdefault( key, {} )
    return res

Dies würde verwendet werden als:

get_leaf_dict( dict, ['Person', 'address', 'city']) = 'New York'

Dies könnte mit Fehlerbehandlung aufgeräumt werden, und eine solche, bei der *args anstelle eines einzelnen Schlüssellistenarguments verwendet wird, könnte hilfreich sein. Die Idee ist jedoch, dass Sie über die Schlüssel iterieren und auf jeder Ebene das entsprechende Wörterbuch aufrufen können.

4
Dave

Hier ist meine einfache Lösung: schreibe einfach

terms = ['person', 'address', 'city'] 
result = nested_dict(3, str)
result[terms] = 'New York'  # as easy as it can be

Sie können sogar tun:

terms = ['John', 'Tinkoff', '1094535332']  # account in Tinkoff Bank
result = nested_dict(3, float)
result[terms] += 2375.30

Nun die Backstage:

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))
2
Fancy John

Hier ist eine andere Option:

from collections import defaultdict
recursivedict = lambda: defaultdict(recursivedict)
mydict = recursivedict()

Das habe ich ursprünglich von hier bekommen: https://stackoverflow.com/a/10218517/1530754 .

Ganz schlau und elegant, wenn Sie mich fragen.

2
Ryan

Ich habe mir die Freiheit genommen, den Code aus der Antwort von Bakuriu zu erweitern. Daher sind Upvotes dazu optional, da sein Code an und für sich eine witzige Lösung ist, an die ich nicht gedacht hätte.

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

Wenn Sie create_missing auf True setzen, müssen Sie sicherstellen, dass nur bereits vorhandene Werte festgelegt werden:

# 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}}
0
user3469861

Verwenden Sie diese beiden Methoden

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]
0
nehemiah

Hier ist eine Variante von Bakurius Antwort, die nicht auf einer separaten Funktion beruht:

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'}}}