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.

2voto

Lennart Regebro Punkte 157632

In Python gibt es viele Möglichkeiten, das Verhalten von Objekten etwas anders zu gestalten, wie z.B. Metaklassen und dergleichen, und es kann getattr und dadurch "magische" Eigenschaften haben, die man nicht durchschauen kann Diktat usw. Kurz gesagt, es ist unwahrscheinlich, dass Sie im allgemeinen Fall mit der von Ihnen verwendeten Methode ein 100%iges Bild erhalten werden.

Daher lautet die Antwort: Wenn es für Sie in Ihrem jetzigen Anwendungsfall funktioniert, dann ist der Code korrekt ;-)

Um einen etwas allgemeineren Code zu erstellen, könnten Sie etwas wie folgt tun:

import types
def todict(obj):
    # Functions, methods and None have no further info of interest.
    if obj is None or isinstance(subobj, (types.FunctionType, types.MethodType))
        return obj

    try: # If it's an iterable, return all the contents
        return [todict(x) for x in iter(obj)]
    except TypeError:
        pass

    try: # If it's a dictionary, recurse over it:
        result = {}
        for key in obj:
            result[key] = todict(obj)
        return result
    except TypeError:
        pass

    # It's neither a list nor a dict, so it's a normal object.
    # Get everything from dir and __dict__. That should be most things we can get hold of.
    attrs = set(dir(obj))
    try:
        attrs.update(obj.__dict__.keys())
    except AttributeError:
        pass

    result = {}
    for attr in attrs:
        result[attr] = todict(getattr(obj, attr, None))
    return result            

So etwas in der Art. Dieser Code ist allerdings ungetestet. Dies deckt immer noch nicht den Fall ab, wenn Sie überschreiben getattr und ich bin sicher, dass es noch viele weitere Fälle gibt, die nicht abgedeckt sind und möglicherweise nicht abgedeckt werden können :)

2voto

aminkr Punkte 11

Es ist keine eigene Implementierung erforderlich. jsons Bibliothek verwendet werden kann.

import jsons

object_dict = jsons.dump(object_instance)

1voto

Alexey Korolkov Punkte 91

Danke @AnuragUniyal! Du hast meinen Tag gerettet! Dies ist meine Variante des Codes, die für mich funktioniert:

# noinspection PyProtectedMember
def object_to_dict(obj):
    data = {}
    if getattr(obj, '__dict__', None):
        for key, value in obj.__dict__.items():
            try:
                data[key] = object_to_dict(value)
            except AttributeError:
                data[key] = value
        return data
    else:
        return obj

0voto

oroszgy Punkte 123

Eine kleine Aktualisierung der Antwort von Shabbyrobe, damit sie auch für namedtuple s:

def obj2dict(obj, classkey=None):
    if isinstance(obj, dict):
        data = {}
        for (k, v) in obj.items():
            data[k] = obj2dict(v, classkey)
        return data
    elif hasattr(obj, "_asdict"):
        return obj2dict(obj._asdict())
    elif hasattr(obj, "_ast"):
        return obj2dict(obj._ast())
    elif hasattr(obj, "__iter__"):
        return [obj2dict(v, classkey) for v in obj]
    elif hasattr(obj, "__dict__"):
        data = dict([(key, obj2dict(value, classkey))
                     for key, value in obj.__dict__.iteritems()
                     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 obj

0voto

def list_object_to_dict(lst):
    return_list = []
    for l in lst:
        return_list.append(object_to_dict(l))
    return return_list

def object_to_dict(object):
    dict = vars(object)
    for k,v in dict.items():
        if type(v).__name__ not in ['list', 'dict', 'str', 'int', 'float']:
                dict[k] = object_to_dict(v)
        if type(v) is list:
            dict[k] = list_object_to_dict(v)
    return dict

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