690 Stimmen

Verschachtelte Python-Diktate in Objekte umwandeln?

Ich bin auf der Suche nach einem eleganten Weg, um Daten mit Attribut Zugriff auf ein Diktat mit einigen verschachtelten Dicts und Listen (d.h. Javascript-Stil Objekt-Syntax) zu erhalten.

Zum Beispiel:

>>> d = {'a': 1, 'b': {'c': 2}, 'd': ["hi", {'foo': "bar"}]}

Sollte auf diese Weise zugänglich sein:

>>> x = dict2obj(d)
>>> x.a
1
>>> x.b.c
2
>>> x.d[1].foo
bar

Ich denke, dies ist nicht möglich, ohne Rekursion, aber was wäre ein schöner Weg, um ein Objekt-Stil für Dicts zu erhalten?

6 Stimmen

Ich habe vor kurzem versucht, etwas Ähnliches zu tun, aber ein wiederkehrender Wörterbuchschlüssel ("from" - ein Python-Schlüsselwort) hat mich daran gehindert, es zu tun. Denn sobald man versuchte, mit "x.from" auf dieses Attribut zuzugreifen, erhielt man einen Syntaxfehler.

4 Stimmen

Das ist in der Tat ein Problem, aber ich kann auf "from" verzichten, um das Leben einfacher zu machen, wenn ich auf große Diktatkonstrukte zugreife:) die Eingabe von x['a']['d'][1]['foo'] ist wirklich lästig, also gilt x.a.d[1].foo. wenn Sie from benötigen, können Sie über getattr(x, 'from') darauf zugreifen oder stattdessen _from als Attribut verwenden.

8 Stimmen

from_ statt _from laut PEP 8 .

3voto

hobs Punkte 16670

Wie wäre es, wenn Sie einfach Ihre dict zum __dict__ eines leeren Objekts?

class Object:
    """If your dict is "flat", this is a simple way to create an object from a dict

    >>> obj = Object()
    >>> obj.__dict__ = d
    >>> d.a
    1
    """
    pass

Natürlich scheitert dies bei Ihrem verschachtelten Diktat-Beispiel, es sei denn, Sie gehen das Diktat rekursiv durch:

# For a nested dict, you need to recursively update __dict__
def dict2obj(d):
    """Convert a dict to an object

    >>> d = {'a': 1, 'b': {'c': 2}, 'd': ["hi", {'foo': "bar"}]}
    >>> obj = dict2obj(d)
    >>> obj.b.c
    2
    >>> obj.d
    ["hi", {'foo': "bar"}]
    """
    try:
        d = dict(d)
    except (TypeError, ValueError):
        return d
    obj = Object()
    for k, v in d.iteritems():
        obj.__dict__[k] = dict2obj(v)
    return obj

Und Ihr Beispiel-Listenelement war wahrscheinlich als ein Mapping eine Liste von (Schlüssel, Wert) Paaren wie diese:

>>> d = {'a': 1, 'b': {'c': 2}, 'd': [("hi", {'foo': "bar"})]}
>>> obj = dict2obj(d)
>>> obj.d.hi.foo
"bar"

2voto

25b3nk Punkte 144
class Dict2Obj:
    def __init__(self, json_data):
        self.convert(json_data)

    def convert(self, json_data):
        if not isinstance(json_data, dict):
            return
        for key in json_data:
            if not isinstance(json_data[key], dict):
                self.__dict__.update({key: json_data[key]})
            else:
                self.__dict__.update({ key: Dict2Obj(json_data[key])})

Ich konnte nicht finden, die Umsetzung der verschachtelten Wörterbuch zu Objekt, so schrieb ein.

Verwendung:

>>> json_data = {"a": {"b": 2}, "c": 3}
>>> out_obj = Dict2Obj(json_data)
>>> out_obj.a
<Dict2Obj object at 0x7f3dc22c2d68>
>>> out_obj.a.b
2
>>> out_obj.a.c
3

2voto

David_li Punkte 89

Konvertieren dict a object

from types import SimpleNamespace

def dict2obj(data):
    """"""
    if not isinstance(data, dict):
        raise ValueError('data must be dict object.')

    def _d2o(d):
        _d = {}
        for key, item in d.items():
            if isinstance(item, dict):
                _d[key] = _d2o(item)
            else:
                _d[key] = item
        return SimpleNamespace(**_d)

    return _d2o(data)

Referenz Antwort

2voto

hokage555 Punkte 63

Suche nach einer einfachen Wrapper-Klasse für dict Ermöglichung des Zugriffs auf bzw. der Zuweisung von Attributen (Punktnotation) Ich war mit den bestehenden Optionen aus den folgenden Gründen nicht zufrieden.

dataclasses , pydantic usw. sind großartig, erfordern aber eine statische Definition des Inhalts. Außerdem können sie nicht ersetzen dict in Code, der sich auf dict da sie nicht dieselben Methoden und __getitem__() Syntax wird nicht unterstützt.

Daher habe ich Folgendes entwickelt MetaDict . Es verhält sich genau wie dict sondern ermöglicht die Punktnotation und die automatische IDE-Vervollständigung (wenn das Objekt im RAM geladen ist) ohne die Unzulänglichkeiten und potenziellen Namensraumkonflikte anderer Lösungen. Alle Funktionen und Anwendungsbeispiele können auf GitHub gefunden werden (siehe Link oben).

Vollständige Offenlegung: Ich bin der Autor von MetaDict .

Unzulänglichkeiten/Einschränkungen, auf die ich beim Ausprobieren anderer Lösungen gestoßen bin:

  • Süchtig
    • Keine automatische Tastenvervollständigung in der IDE
    • Verschachtelte Tastenbelegung kann nicht ausgeschaltet werden
    • Neu zugewiesen dict Objekte werden nicht so konvertiert, dass sie den Schlüsselzugriff im Attributstil unterstützen
    • Eingebauter Schattentyp Dict
  • Prodikt
    • Keine automatische Schlüsselvervollständigung in der IDE ohne Definition eines statischen Schemas (ähnlich wie bei dataclass )
    • Keine rekursive Umwandlung von dict Objekte, wenn sie eingebettet sind in list oder andere eingebaute Iterabilien
  • AttrDict
    • Keine automatische Tastenvervollständigung in der IDE
    • Konvertiert list Objekte zu tuple hinter den Kulissen
  • Munch
    • Eingebaute Methoden wie items() , update() usw. können überschrieben werden mit obj.items = [1, 2, 3]
    • Keine rekursive Umwandlung von dict Objekte, wenn sie eingebettet sind in list oder andere eingebaute Iterabilien
  • EasyDict
    • Nur Strings sind gültige Schlüssel, aber dict akzeptiert alle hashfähigen Objekte als Schlüssel
    • Eingebaute Methoden wie items() , update() usw. können überschrieben werden mit obj.items = [1, 2, 3]
    • Eingebaute Methoden verhalten sich nicht wie erwartet: obj.pop('unknown_key', None) wirft ein AttributeError

Hinweis: Ich habe eine ähnliche Antwort in diesem Stackoverflow der damit zusammenhängt.

2voto

tcpiper Punkte 2148

Ich denke, dass ein Diktat, das aus Zahl, String und Diktat besteht, meistens ausreicht. Daher ignoriere ich die Situation, dass Tupel, Listen und andere Typen in der letzten Dimension eines Diktats nicht vorkommen.

Unter Berücksichtigung der Vererbung, kombiniert mit Rekursion, löst es das Druckproblem auf bequeme Weise und bietet außerdem zwei Möglichkeiten zur Abfrage von Daten und eine Möglichkeit zur Bearbeitung von Daten.

Siehe das folgende Beispiel, ein Diktat, das einige Informationen über Schüler beschreibt:

group=["class1","class2","class3","class4",]
rank=["rank1","rank2","rank3","rank4","rank5",]
data=["name","sex","height","weight","score"]

#build a dict based on the lists above
student_dic=dict([(g,dict([(r,dict([(d,'') for d in data])) for r in rank ]))for g in group])

#this is the solution
class dic2class(dict):
    def __init__(self, dic):
        for key,val in dic.items():
            self.__dict__[key]=self[key]=dic2class(val) if isinstance(val,dict) else val

student_class=dic2class(student_dic)

#one way to edit:
student_class.class1.rank1['sex']='male'
student_class.class1.rank1['name']='Nan Xiang'

#two ways to query:
print student_class.class1.rank1
print student_class.class1['rank1']
print '-'*50
for rank in student_class.class1:
    print getattr(student_class.class1,rank)

Ergebnisse:

{'score': '', 'sex': 'male', 'name': 'Nan Xiang', 'weight': '', 'height': ''}
{'score': '', 'sex': 'male', 'name': 'Nan Xiang', 'weight': '', 'height': ''}
--------------------------------------------------
{'score': '', 'sex': '', 'name': '', 'weight': '', 'height': ''}
{'score': '', 'sex': '', 'name': '', 'weight': '', 'height': ''}
{'score': '', 'sex': 'male', 'name': 'Nan Xiang', 'weight': '', 'height': ''}
{'score': '', 'sex': '', 'name': '', 'weight': '', 'height': ''}
{'score': '', 'sex': '', 'name': '', 'weight': '', 'height': ''}

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