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 .

1voto

Whospal Punkte 51

Mein Wörterbuch ist von diesem Format:

addr_bk = {
    'person': [
        {'name': 'Andrew', 'id': 123, 'email': 'andrew@mailserver.com',
         'phone': [{'type': 2, 'number': '633311122'},
                   {'type': 0, 'number': '97788665'}]
        },
        {'name': 'Tom', 'id': 456,
         'phone': [{'type': 0, 'number': '91122334'}]}, 
        {'name': 'Jack', 'id': 7788, 'email': 'jack@gmail.com'}
    ]
}

Wie Sie sehen können, habe ich verschachtelte Wörterbücher y Liste von Diktaten . Dies liegt daran, dass addr_bk aus Protokollpufferdaten dekodiert wurde, die mit lwpb.codec in ein Python-Dict umgewandelt wurden. Es gibt optionale Felder (z.B. email => wo der Schlüssel nicht verfügbar sein kann) und wiederholte Felder (z.B. phone => konvertiert in eine Liste von dict).

Ich habe alle oben vorgeschlagenen Lösungen ausprobiert. Einige kommen mit den verschachtelten Wörterbüchern nicht zurecht. Andere können die Objektdetails nicht einfach drucken.

Nur die Lösung dict2obj(dict) von Dawie Strauss funktioniert am besten.

Ich habe die Funktion ein wenig erweitert, damit sie funktioniert, wenn der Schlüssel nicht gefunden wird:

# Work the best, with nested dictionaries & lists! :)
# Able to print out all items.
class dict2obj_new(dict):
    def __init__(self, dict_):
        super(dict2obj_new, self).__init__(dict_)
        for key in self:
            item = self[key]
            if isinstance(item, list):
                for idx, it in enumerate(item):
                    if isinstance(it, dict):
                        item[idx] = dict2obj_new(it)
            elif isinstance(item, dict):
                self[key] = dict2obj_new(item)

    def __getattr__(self, key):
        # Enhanced to handle key not found.
        if self.has_key(key):
            return self[key]
        else:
            return None

Dann habe ich es mit getestet:

# Testing...
ab = dict2obj_new(addr_bk)

for person in ab.person:
  print "Person ID:", person.id
  print "  Name:", person.name
  # Check if optional field is available before printing.
  if person.email:
    print "  E-mail address:", person.email

  # Check if optional field is available before printing.
  if person.phone:
    for phone_number in person.phone:
      if phone_number.type == codec.enums.PhoneType.MOBILE:
        print "  Mobile phone #:",
      elif phone_number.type == codec.enums.PhoneType.HOME:
        print "  Home phone #:",
      else:
        print "  Work phone #:",
      print phone_number.number

1voto

NeuronQ Punkte 6547

Ich war nicht zufrieden mit den markierten und hochgestuften Antworten, deshalb hier eine simple und allgemein Lösung für die Umwandlung von verschachtelten Datenstrukturen im JSON-Stil (bestehend aus Dicts und Listen) in Hierarchien von einfachen Objekten:

# tested in: Python 3.8
from collections import abc
from typings import Any, Iterable, Mapping, Union

class DataObject:
    def __repr__(self):
        return str({k: v for k, v in vars(self).items()})

def data_to_object(data: Union[Mapping[str, Any], Iterable]) -> object:
    """
    Example
    -------
    >>> data = {
    ...     "name": "Bob Howard",
    ...     "positions": [{"department": "ER", "manager_id": 13}],
    ... }
    ... data_to_object(data).positions[0].manager_id
    13
    """
    if isinstance(data, abc.Mapping):
        r = DataObject()
        for k, v in data.items():
            if type(v) is dict or type(v) is list:
                setattr(r, k, data_to_object(v))
            else:
                setattr(r, k, v)
        return r
    elif isinstance(data, abc.Iterable):
        return [data_to_object(e) for e in data]
    else:
        return data

1voto

Max Sirwa Punkte 135

Aufbauend auf dem, was zuvor durch die akzeptierte Antwort getan wurde, wenn Sie es rekursiv haben möchten.

class FullStruct:
    def __init__(self, **kwargs):
        for key, value in kwargs.items():
            if isinstance(value, dict):
                f = FullStruct(**value)
                self.__dict__.update({key: f})
            else:
                self.__dict__.update({key: value})

1voto

Sie können es auf meine Weise handhaben.

somedict= {"person": {"name": "daniel"}}

class convertor:
    def __init__(self, dic: dict) -> object:
        self.dict = dic

        def recursive_check(obj):
            for key, value in dic.items():
                if isinstance(value, dict):
                    value= convertor(value)
                setattr(obj, key, value)
        recursive_check(self)
my_object= convertor(somedict)

print(my_object.person.name)

0voto

thiago marini Punkte 450

Diese kleine Klasse bereitet mir keine Probleme, erweitern Sie sie einfach und verwenden Sie die copy()-Methode:

  import simplejson as json

  class BlindCopy(object):

    def copy(self, json_str):
        dic = json.loads(json_str)
        for k, v in dic.iteritems():
            if hasattr(self, k):
                setattr(self, k, v);

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