递归转换Python对象图为字典
我正在尝试把一个简单的对象图的数据转换成字典。我不需要类型信息,也不需要方法,更不需要把它再转换回对象。
我找到了一些关于如何从对象的字段创建字典的问题,但它们并没有递归处理。
因为我对Python还比较陌生,所以我担心我的解决方案可能不太好,或者不符合Python的风格,甚至可能在某些不明显的地方出问题,或者只是单纯的“自己动手”。
我第一次尝试的代码看起来能工作,但当我用列表和字典测试时,似乎更简单的方法是检查传入的对象是否有内部字典,如果没有,就把它当作一个值来处理(而不是做那么多类型检查)。我之前的尝试也没有递归处理对象列表:
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
这个方法似乎效果更好,而且不需要处理异常,但我还是不确定有没有我不知道的情况会出问题。
如果有任何建议,我会非常感激。
14 个回答
我不太明白为什么要检查一个东西是不是基础字符串(basestring)或者对象(object)呢?而且字典(dict)里不会包含任何可调用的东西,除非你有属性指向这些可调用的东西,但那样的话不就算是对象的一部分了吗?
所以与其检查各种类型和数值,不如让todict来转换这个对象,如果它抛出异常,那就用原来的值。
todict 只有在对象没有字典(dict)的时候才会抛出异常,比如:
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, 'o1': {'a1': 1}}。可能还有其他情况需要考虑,但这可能是个不错的开始。
特殊情况:如果一个对象使用了槽(slots),那么你就无法获取字典(dict),例如:
class A(object):
__slots__ = ["a1"]
def __init__(self):
self.a1 = 1
解决槽的情况可以使用 dir(),而不是直接使用字典(dict)。
这段代码可以把一个对象转换成JSON格式,并且会递归处理对象里的所有内容。
import json
def get_json(obj):
return json.loads(
json.dumps(obj, default=lambda o: getattr(o, '__dict__', str(o)))
)
obj = SomeClass()
print("Json = ", get_json(obj))
这是我自己尝试的结果,结合了Anurag Uniyal和Lennart 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