481 Stimmen

Wie verwende ich einen Punkt "." um auf Elemente eines Wörterbuchs zuzugreifen?

Wie mache ich Python-Wörterbuchmitglieder über einen Punkt "." zugänglich?

Zum Beispiel würde ich gerne statt mydict['val'] mydict.val schreiben.

Außerdem würde ich gerne auf verschachtelte Wörterbücher auf diese Weise zugreifen. Zum Beispiel

mydict.mydict2.val 

würde sich auf

mydict = { 'mydict2': { 'val': ... } }

32 Stimmen

Viele der Situationen, in denen Menschen verschachtelte Dicts verwenden, könnten genauso gut oder sogar besser durch Dicts mit Tupeln als Schlüssel gelöst werden, wobei d[a][b][c] durch d[a, b, c] ersetzt wird.

0 Stimmen

Können Sie bitte näher erläutern, wie d[a][b][c] durch d[a, b, c] ersetzt werden kann? Ich habe noch nicht verstanden, wie das gemacht werden kann.

9 Stimmen

Es ist kein Zauberei: foo={}; foo[1,2,3] = "one, two, three!"; foo.keys() => [(1,2,3)]

6voto

rv.kvetch Punkte 4720

Ich bin kein großer Fan davon, noch mehr Holz auf ein (über) 10 Jahre altes Feuer zu legen, aber ich würde mir auch die dotwiz Bibliothek ansehen, die ich vor Kurzem veröffentlicht habe - erst in diesem Jahr tatsächlich.

Es handelt sich um eine ziemlich kleine Bibliothek, die auch in Get (Zugriff) und Set (Erstellen) Zeiten bei Benchmarks wirklich gut abschneidet, zumindest im Vergleich zu anderen Alternativen.

Installiere dotwiz mit pip

pip install dotwiz

Es macht alles, was du von ihm erwartest und leitet von dict ab, sodass es wie ein normales Wörterbuch funktioniert:

from dotwiz import DotWiz

dw = DotWiz()
dw.hello = 'world'
dw.hello
dw.hello += '!'
# dw.hello und dw['hello'] liefern jetzt beide 'world!'
dw.val = 5
dw.val2 = 'Sam'

Zusätzlich kannst du es in und aus dict Objekte umwandeln:

d = dw.to_dict()
dw = DotWiz(d) # automatische Konvertierung im Konstruktor

Das bedeutet, dass wenn etwas, auf das du zugreifen möchtest, bereits in dict Form vorliegt, kannst du es in ein DotWiz für einen einfachen Zugriff umwandeln:

import json
json_dict = json.loads(text)
data = DotWiz(json_dict)
print data.location.city

Und schließlich arbeite ich auch an einer bestehenden Feature-Anfrage, um automatisch neue Kind-DotWiz Instanzen zu erstellen, damit du Dinge wie folgendes tun kannst:

dw = DotWiz()
dw['people.steve.age'] = 31

dw
# (people=(steve=(age=31)))

Vergleich mit dotmap

Ich habe einen schnellen und einfachen Leistungsvergleich mit dotmap unten hinzugefügt.

Installiere zunächst beide Bibliotheken mit pip:

pip install dotwiz dotmap

Das folgende Codebeispiel habe ich für Benchmark-Zwecke entwickelt:

from timeit import timeit

from dotwiz import DotWiz
from dotmap import DotMap

d = {'hey': {'so': [{'this': {'is': {'pretty': {'cool': True}}}}]}

dw = DotWiz(d)
# (hey=(so=[(this=(is=(pretty={'cool'})))]))

dm = DotMap(d)
# DotMap(hey=DotMap(so=[DotMap(this=DotMap(is=DotMap(pretty={'cool'})))]))

assert dw.hey.so[0].this['is'].pretty.cool == dm.hey.so[0].this['is'].pretty.cool

n = 100_000

print('dotwiz (erstellen):  ', round(timeit('DotWiz(d)', number=n, globals=globals()), 3))
print('dotmap (erstellen):  ', round(timeit('DotMap(d)', number=n, globals=globals()), 3))
print('dotwiz (zugriff):  ', round(timeit("dw.hey.so[0].this['is'].pretty.cool", number=n, globals=globals()), 3))
print('dotmap (zugriff):  ', round(timeit("dm.hey.so[0].this['is'].pretty.cool", number=n, globals=globals()), 3))

Ergebnisse auf meinem M1 Mac mit Python 3.10:

dotwiz (erstellen):   0.189
dotmap (erstellen):   1.085
dotwiz (zugriff):   0.014
dotmap (zugriff):   0.335

6voto

pbanka Punkte 677

Die Sprache selbst unterstützt das nicht, aber manchmal ist dies dennoch eine nützliche Anforderung. Neben dem Bunch-Rezept können Sie auch eine kleine Methode schreiben, die über einen Punktstring auf ein Wörterbuch zugreifen kann:

def get_var(input_dict, accessor_string):
    """Ruft Daten aus einem Wörterbuch unter Verwendung eines Punkt-Accessor-Strings ab"""
    current_data = input_dict
    for chunk in accessor_string.split('.'):
        current_data = current_data.get(chunk, {})
    return current_data

was etwas wie dies unterstützen würde:

>> test_dict = {'thing': {'spam': 12, 'foo': {'cheeze': 'bar'}}}
>> output = get_var(test_dict, 'thing.spam.foo.cheeze')
>> print output
'bar'
>>

4voto

Yaniv K. Punkte 177

Dies funktioniert auch mit verschachtelten dicts und stellt sicher, dass dicts, die später angehängt werden, sich genauso verhalten:

class DotDict(dict):

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        # Verschachtelte dicts rekursiv in DotDicts umwandeln
        for key, value in self.items():
            if type(value) is dict:
                self[key] = DotDict(value)

    def __setitem__(self, key, item):
        if type(item) is dict:
            item = DotDict(item)
        super().__setitem__(key, item)

    __setattr__ = __setitem__
    __getattr__ = dict.__getitem__

4voto

JayD3e Punkte 2057

Ich habe versucht, BEIDE die AttrDict und die Bunch Bibliotheken auszuprobieren und fand sie für meine Zwecke viel zu langsam. Nachdem ein Freund und ich uns damit befasst hatten, stellten wir fest, dass die Hauptmethode zum Schreiben dieser Bibliotheken dazu führt, dass die Bibliothek aggressiv durch ein verschachteltes Objekt rekursiert und Kopien des Wörterbuchobjekts erstellt. Vor diesem Hintergrund haben wir zwei wesentliche Änderungen vorgenommen. 1) Wir haben Attribute lazy-loaded gemacht 2) anstelle von Kopien eines Wörterbuchobjekts erstellen wir Kopien eines leichtgewichtigen Proxy-Objekts. Dies ist die endgültige Implementierung. Die Leistungssteigerung durch die Verwendung dieses Codes ist unglaublich. Bei der Verwendung von AttrDict oder Bunch haben allein diese zwei Bibliotheken jeweils 1/2 und 1/3 meiner Anforderungszeit verbraucht (was!?). Dieser Code reduzierte diese Zeit auf fast nichts (irgendwo im Bereich von 0,5 ms). Dies hängt natürlich von Ihren Bedürfnissen ab, aber wenn Sie diese Funktionalität in Ihrem Code häufig verwenden, entscheiden Sie sich auf jeden Fall für etwas Einfaches wie dies.

class DictProxy(object):
    def __init__(self, obj):
        self.obj = obj

    def __getitem__(self, key):
        return wrap(self.obj[key])

    def __getattr__(self, key):
        try:
            return wrap(getattr(self.obj, key))
        except AttributeError:
            try:
                return self[key]
            except KeyError:
                raise AttributeError(key)

    # wahrscheinlich möchten Sie auch wichtige Listenattribute wie
    # items(), iteritems() und __len__ weiterleiten

class ListProxy(object):
    def __init__(self, obj):
        self.obj = obj

    def __getitem__(self, key):
        return wrap(self.obj[key])

    # wahrscheinlich möchten Sie auch wichtige Listenattribute wie
    # __iter__ und __len__ weiterleiten

def wrap(value):
    if isinstance(value, dict):
        return DictProxy(value)
    if isinstance(value, (tuple, list)):
        return ListProxy(value)
    return value

Sehen Sie sich die ursprüngliche Implementierung hier von https://stackoverflow.com/users/704327/michael-merickel an.

Eine weitere Sache zu beachten ist, dass diese Implementierung ziemlich einfach ist und nicht alle Methoden implementiert, die möglicherweise erforderlich sind. Sie müssen diese bei Bedarf für die DictProxy- oder ListProxy-Objekte schreiben.

4voto

Gulzar Punkte 16632

Für unendliche Verschachtelungsebenen von Dicts, Listen, Listen von Dicts und Dicts von Listen.

Es unterstützt auch das Pickling.

Dies ist eine Erweiterung der dieser Antwort.

class DotDict(dict):
    # https://stackoverflow.com/a/70665030/913098
    """
    Beispiel:
    m = Map({'first_name': 'Eduardo'}, last_name='Pool', age=24, sports=['Soccer'])

    Iterable wird angenommen, dass sie einen Konstruktor hat, der eine Liste als Eingabe akzeptiert.
    """

    def __init__(self, *args, **kwargs):
        super(DotDict, self).__init__(*args, **kwargs)

        args_with_kwargs = []
        for arg in args:
            args_with_kwargs.append(arg)
        args_with_kwargs.append(kwargs)
        args = args_with_kwargs

        for arg in args:
            if isinstance(arg, dict):
                for k, v in arg.items():
                    self[k] = v
                    if isinstance(v, dict):
                        self[k] = DotDict(v)
                    elif isinstance(v, str) or isinstance(v, bytes):
                        self[k] = v
                    elif isinstance(v, Iterable):
                        klass = type(v)
                        map_value: List[Any] = []
                        for e in v:
                            map_e = DotDict(e) if isinstance(e, dict) else e
                            map_value.append(map_e)
                        self[k] = klass(map_value)

    def __getattr__(self, attr):
        return self.get(attr)

    def __setattr__(self, key, value):
        self.__setitem__(key, value)

    def __setitem__(self, key, value):
        super(DotDict, self).__setitem__(key, value)
        self.__dict__.update({key: value})

    def __delattr__(self, item):
        self.__delitem__(item)

    def __delitem__(self, key):
        super(DotDict, self).__delitem__(key)
        del self.__dict__[key]

    def __getstate__(self):
        return self.__dict__

    def __setstate__(self, d):
        self.__dict__.update(d)

if __name__ == "__main__":
    import pickle
    def test_map():
        d = {
            "a": 1,
            "b": {
                "c": "d",
                "e": 2,
                "f": None
            },
            "g": [],
            "h": [1, "i"],
            "j": [1, "k", {}],
            "l":
                [
                    1,
                    "m",
                    {
                        "n": [3],
                        "o": "p",
                        "q": {
                            "r": "s",
                            "t": ["u", 5, {"v": "w"}, ],
                            "x": ("z", 1)
                        }
                    }
                ],
        }
        map_d = DotDict(d)
        w = map_d.l[2].q.t[2].v
        assert w == "w"

        pickled = pickle.dumps(map_d)
        unpickled = pickle.loads(pickled)
        assert unpickled == map_d

        kwargs_check = DotDict(a=1, b=[dict(c=2, d="3"), 5])
        assert kwargs_check.b[0].d == "3"

        kwargs_and_args_check = DotDict(d, a=1, b=[dict(c=2, d="3"), 5])
        assert kwargs_and_args_check.l[2].q.t[2].v == "w"
        assert kwargs_and_args_check.b[0].d == "3"

    test_map()

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X