递归转换Python对象图表字典(Recursively convert python object

2019-06-24 01:03发布

我试图将数据从一个简单的对象图转换成字典。 我不需要类型信息或方法,我也不需要能够再次转换回一个对象。

我发现有关创建从对象的字段字典这个问题 ,但它并没有这样做递归。

作为比较新的蟒蛇,我担心我的解决方案可能是丑陋的,或者unpythonic,或在一些不起眼的方式打破,或只是普通的老NIH。

我第一次尝试似乎工作,直到我有列表和字典试了一下,它似乎更容易只是为了检查是否传递的对象有一个内部字典,如果没有,只是把它当作一个值(而不是做一切isinstance检查)。 我以前的尝试也没有递归到对象的列表:

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

这似乎更好地工作,而且不需要例外,但我又真不知道,如果有这样的情况在这里,我不知道它在哪里跌倒。

任何建议将不胜感激。

Answer 1:

从阿努拉格Uniyal和伦纳特Regebro的答案得出我自己的尝试和线索的合并最适合我:

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


Answer 2:

一号线代码对象转换成JSON递归

import json
print(json.dumps(a, default=lambda o: getattr(o, '__dict__', str(o))))


Answer 3:

我不知道什么是检查即basestring或对象的目的是什么? 字典也将不包含任何可调用,除非你有指向这样的可调用属性,但在这种情况下是不是对象的一部分?

所以不是检查各种类型和价值观,让todict转换的对象,如果它引发异常,用户的原单值。

todict只会引发异常如果obj没有字典

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)

它打印{“B1”:1,“B2”:2,“01”:{“A1”:1}}可能会有一些其他的情况需要考虑,但它可能是一个良好的开端

如有特殊情况下 ,对象使用插槽,那么你将无法得到如字典

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

定为槽的情况下可以使用目录(),而不是直接使用dict



Answer 4:

在Python有使物体略微不同的行为,像元类和诸如此类的许多方面,它可以覆盖GETATTR,从而具有“神奇”属性,你可以不通过快译通等,总之看到,这是不可能的,你要与您取得使用任何方法一般情况下,100%的完整画面。

因此,答案是:它是否适合你,你现在有使用的情况下,则代码是正确的。 ;-)

为了让稍微更通用的代码,你可以做这样的事情:

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            

类似的东西。 该代码是未经测试,但。 这仍然当你重写GETATTR没有覆盖的情况下,我敢肯定还有更多的情况下,它好好尝试一下盖,可能不千分之二。 :)



Answer 5:

缓慢但简单的方法来做到这一点是使用jsonpickle对象转换为JSON字符串,然后json.loads将其转换回Python字典:

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



Answer 6:

我知道这答案是几年为时已晚,但我想这可能是值得分享,因为它是一个Python 3.3+兼容的修改由@Shabbyrobe原来的解决方案,一向运作良好,对我:

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

如果你在调用属性不感兴趣,例如,它们可以在字典解析剥离:

elif isinstance(obj, dict):
  return dict((key, todict(val)) for key, val in obj.items() if not callable(val))


Answer 7:

一个小更新Shabbyrobe的答案,使之成为工作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


Answer 8:

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


文章来源: Recursively convert python object graph to dictionary