Recursively convert python object graph to dictionary

前端 未结 9 1555
既然无缘
既然无缘 2020-12-04 10:25

I\'m trying to convert the data from a simple object graph into a dictionary. I don\'t need type information or methods and I don\'t need to be able to convert it back to an

相关标签:
9条回答
  • 2020-12-04 10:40

    I don't know what is the purpose of checking for basestring or object is? also dict will not contain any callables unless you have attributes pointing to such callables, but in that case isn't that part of object?

    so instead of checking for various types and values, let todict convert the object and if it raises the exception, user the orginal value.

    todict will only raise exception if obj doesn't have dict e.g.

    class A(object):
        def __init__(self):
            self.a1 = 1
    
    class B(object):
        def __init__(self):
            self.b1 = 1
            self.b2 = 2
            self.o1 = A()
    
        def func1(self):
            pass
    
    def todict(obj):
        data = {}
        for key, value in obj.__dict__.iteritems():
            try:
                data[key] = todict(value)
            except AttributeError:
                data[key] = value
        return data
    
    b = B()
    print todict(b)
    

    it prints {'b1': 1, 'b2': 2, 'o1': {'a1': 1}} there may be some other cases to consider, but it may be a good start

    special cases if a object uses slots then you will not be able to get dict e.g.

    class A(object):
        __slots__ = ["a1"]
        def __init__(self):
            self.a1 = 1
    

    fix for the slots cases can be to use dir() instead of directly using the dict

    0 讨论(0)
  • 2020-12-04 10:44

    A slow but easy way to do this is to use jsonpickle to convert the object to a JSON string and then json.loads to convert it back to a python dictionary:

    dict = json.loads(jsonpickle.encode( obj, unpicklable=False ))

    0 讨论(0)
  • 2020-12-04 10:46

    An amalgamation of my own attempt and clues derived from Anurag Uniyal and Lennart Regebro's answers works best for me:

    def todict(obj, classkey=None):
        if isinstance(obj, dict):
            data = {}
            for (k, v) in obj.items():
                data[k] = todict(v, classkey)
            return data
        elif hasattr(obj, "_ast"):
            return todict(obj._ast())
        elif hasattr(obj, "__iter__") and not isinstance(obj, str):
            return [todict(v, classkey) for v in obj]
        elif hasattr(obj, "__dict__"):
            data = dict([(key, todict(value, 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 obj
    
    0 讨论(0)
  • 2020-12-04 10:54

    I realize that this answer is a few years too late, but I thought it might be worth sharing since it's a Python 3.3+ compatible modification to the original solution by @Shabbyrobe that has generally worked well for me:

    import collections
    try:
      # Python 2.7+
      basestring
    except NameError:
      # Python 3.3+
      basestring = str 
    
    def todict(obj):
      """ 
      Recursively convert a Python object graph to sequences (lists)
      and mappings (dicts) of primitives (bool, int, float, string, ...)
      """
      if isinstance(obj, basestring):
        return 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, '__dict__'):
        return todict(vars(obj))
      elif hasattr(obj, '__slots__'):
        return todict(dict((name, getattr(obj, name)) for name in getattr(obj, '__slots__')))
      return obj
    

    If you're not interested in callable attributes, for example, they can be stripped in the dictionary comprehension:

    elif isinstance(obj, dict):
      return dict((key, todict(val)) for key, val in obj.items() if not callable(val))
    
    0 讨论(0)
  • 2020-12-04 10:54
    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
    
    0 讨论(0)
  • 2020-12-04 10:57

    Looked at all solutions, and @hbristow's answer was closest to what I was looking for. Added enum.Enum handling since this was causing a RecursionError: maximum recursion depth exceeded error and reordered objects with __slots__ to have precedence of objects defining __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
    
    0 讨论(0)
提交回复
热议问题