51 Stimmen

Rekursive Konvertierung eines Python-Objektgraphen in ein Wörterbuch

Ich versuche, die Daten aus einem einfachen Objektdiagramm in ein Wörterbuch zu konvertieren. Ich brauche keine Typinformationen oder Methoden und muss nicht in der Lage sein, sie wieder in ein Objekt zu konvertieren.

Ich fand diese Frage zur Erstellung eines Wörterbuchs aus den Feldern eines Objekts aber er tut es nicht rekursiv.

Da ich relativ neu in Python bin, bin ich besorgt, dass meine Lösung hässlich, oder unpythonisch, oder in irgendeiner obskuren Weise gebrochen, oder einfach nur alt NIH sein kann.

Mein erster Versuch schien zu funktionieren, bis ich es mit Listen und Wörterbüchern versuchte, und es schien einfacher, nur zu prüfen, ob das übergebene Objekt ein internes Wörterbuch hatte, und wenn nicht, es einfach als Wert zu behandeln (anstatt all das zu tun, isinstance Überprüfung). Meine früheren Versuche haben auch nicht in Listen von Objekten rekursiert:

def todict(obj):
    if hasattr(obj, "__iter__"):
        return [todict(v) for v in obj]
    elif hasattr(obj, "__dict__"):
        return dict([(key, todict(value)) 
            for key, value in obj.__dict__.iteritems() 
            if not callable(value) and not key.startswith('_')])
    else:
        return obj

Dies scheint besser zu funktionieren und erfordert keine Ausnahmen, aber auch hier bin ich mir nicht sicher, ob es Fälle gibt, die ich nicht kenne, in denen dies nicht funktioniert.

Für Vorschläge wären wir sehr dankbar.

0voto

Will Punkte 2678

Ich habe mir alle Lösungen angesehen, und die Antwort von @hbristow kam dem am nächsten, wonach ich gesucht habe. Hinzugefügt enum.Enum Handhabung, da dies zu einer RecursionError: maximum recursion depth exceeded Fehler und neu geordnete Objekte mit __slots__ Vorrang vor Objekten haben, die __dict__ .

def todict(obj):
  """
  Recursively convert a Python object graph to sequences (lists)
  and mappings (dicts) of primitives (bool, int, float, string, ...)
  """
  if isinstance(obj, str):
    return obj
  elif isinstance(obj, enum.Enum):
    return str(obj)
  elif isinstance(obj, dict):
    return dict((key, todict(val)) for key, val in obj.items())
  elif isinstance(obj, collections.Iterable):
    return [todict(val) for val in obj]
  elif hasattr(obj, '__slots__'):
    return todict(dict((name, getattr(obj, name)) for name in getattr(obj, '__slots__')))
  elif hasattr(obj, '__dict__'):
    return todict(vars(obj))
  return obj

0voto

Alex Punkte 31

Ich würde die akzeptierte Antwort kommentieren, aber mein Ansehen ist nicht hoch genug... Die akzeptierte Antwort ist großartig, aber fügen Sie eine weitere hinzu elif kurz nach der if um die Serialisierung von NamedTuples zu unterstützen, damit sie auch richtig diktiert werden können:

    elif hasattr(obj, "_asdict"):
        return todict(obj._asdict())

0voto

Nun. Hinzugefügt Funktionalität der Begrenzung der Tiefe zu @Shabbyrobe Antwort. Dachte, es könnte für die Objekte, die Schleife zurück wert sein.

def todict(obj, limit=sys.getrecursionlimit(), classkey=None):
        if isinstance(obj, dict):
            if limit>=1:
                data = {}
                for (k, v) in obj.items():
                    data[k] = todict(v, limit-1,classkey)
                return data
            else:
                return 'class:'+obj.__class__.__name__
        elif hasattr(obj, "_ast"):
            return todict(obj._ast(), limit-1) if limit>=1 else {'class:'+obj.__class__.__name__}
        elif hasattr(obj, "__iter__") and not isinstance(obj, str):
            return [todict(v, limit-1, classkey) for v in obj] if limit>=1 else {'class:'+obj.__class__.__name__}
        elif hasattr(obj, "__dict__"):
            if limit>=1:
                data = dict([(key, todict(value, limit-1, classkey)) 
                    for key, value in obj.__dict__.items() 
                    if not callable(value) and not key.startswith('_')])
                if classkey is not None and hasattr(obj, "__class__"):
                    data[classkey] = obj.__class__.__name__
                return data
            else:
                return 'class:'+obj.__class__.__name__
        else:
            return obj

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