递归地将Python对象图转换为字典
我正在尝试将数据从简单的对象图转换为字典。 我不需要类型信息或方法,也不需要能够再次将其转换回对象。
我发现这个关于从对象的字段创建字典的问题,但它没有不要递归地进行。
作为对Python相对较新的人,我担心我的解决方案可能很丑陋,或者不符合Python风格,或者以某种晦涩的方式被破坏,或者只是普通的旧NIH。
我的第一次尝试似乎有效,直到我尝试使用列表和字典,并且检查传递的对象是否有内部字典似乎更容易,如果没有,则将其视为一个值(而不是执行所有实例检查) )。 我之前的尝试也没有递归到对象列表中:
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
这似乎工作得更好并且不需要例外,但我仍然不确定这里是否存在我不知道它在哪里失败的情况。
任何建议将不胜感激。
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 object again.
I found this question about creating a dictionary from an object's fields, but it doesn't do it recursively.
Being relatively new to python, I'm concerned that my solution may be ugly, or unpythonic, or broken in some obscure way, or just plain old NIH.
My first attempt appeared to work until I tried it with lists and dictionaries, and it seemed easier just to check if the object passed had an internal dictionary, and if not, to just treat it as a value (rather than doing all that isinstance checking). My previous attempts also didn't recurse into lists of objects:
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
This seems to work better and doesn't require exceptions, but again I'm still not sure if there are cases here I'm not aware of where it falls down.
Any suggestions would be much appreciated.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(14)
我自己的尝试和来自 Anurag Uniyal 和 Lennart Regebro 答案的线索的结合最适合我:
An amalgamation of my own attempt and clues derived from Anurag Uniyal and Lennart Regebro's answers works best for me:
一行代码将对象递归转换为 JSON。
One line of code to convert an object to JSON recursively.
我不知道检查基字符串或对象的目的是什么? 另外 dict 不会包含任何可调用对象,除非您有指向此类可调用对象的属性,但在这种情况下,这不是对象的一部分吗?
因此,不要检查各种类型和值,而是让 todict 转换对象,如果引发异常,则使用原始值。
todict 仅当 obj 没有 dict 时才会引发异常
例如,
它打印 {'b1': 1, 'b2': 2, 'o1': {'a1': 1}}
可能还有一些其他情况需要考虑,但这可能是一个好的开始
特殊情况
如果对象使用插槽,那么您将无法获取 dict 例如,
插槽情况的修复可以是使用 dir() 而不是直接使用 dict
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.
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.
fix for the slots cases can be to use dir() instead of directly using the dict
一种缓慢但简单的方法是使用 jsonpickle 将对象转换为 JSON 字符串,然后使用 json.loads 将其转换回 Python 字典
: >dict = json.loads(jsonpickle.encode( obj, unpicklable=False ))
A slow but easy way to do this is to use
jsonpickle
to convert the object to a JSON string and thenjson.loads
to convert it back to a python dictionary:dict = json.loads(jsonpickle.encode( obj, unpicklable=False ))
我意识到这个答案已经晚了几年,但我认为它可能值得分享,因为它是 @Shabbyrobe 对原始解决方案的 Python 3.3+ 兼容修改,通常对我来说效果很好:
如果你对可调用属性,例如,它们可以在字典理解中被剥离:
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:
If you're not interested in callable attributes, for example, they can be stripped in the dictionary comprehension:
在 Python 中,有很多方法可以使对象的行为略有不同,例如元类等,并且它可以覆盖 getattr ,从而具有您无法通过 dict 查看的“神奇”属性> 等。简而言之,无论您使用什么方法,您都不太可能在一般情况下获得 100% 的完整情况。
因此,答案是:如果它在您现在的用例中适用,那么代码是正确的。 ;-)
要制作更通用的代码,您可以这样做:
类似的事情。 不过,该代码未经测试。 这仍然没有涵盖当您覆盖 getattr 时的情况,而且我确信还有更多的情况它没有涵盖并且可能无法涵盖。 :)
In Python there are many ways of making objects behave slightly differently, like metaclasses and whatnot, and it can override getattr and thereby have "magical" attributes you can't see through dict, etc. In short, it's unlikely that you are going to get a 100% complete picture in the generic case with whatever method you use.
Therefore, the answer is: If it works for you in the use case you have now, then the code is correct. ;-)
To make somewhat more generic code you could do something like this:
Something like that. That code is untested, though. This still doesn't cover the case when you override getattr, and I'm sure there are many more cases that it doens't cover and may not be coverable. :)
不需要自定义实现。 可以使用 jsons 库。
No custom implementation is required. jsons library can be used.
谢谢@AnuragUniyal!
你让我今天一整天都感觉很好!
这是我的代码变体,对我有用:
Thanks @AnuragUniyal!
You made my day!
This is my variant of code that's working for me:
对 Shabbyrobe 的答案进行一点更新,使其适用于
namedtuple
:A little update to Shabbyrobe's answer to make it work for
namedtuple
s:查看了所有解决方案,@hbristow 的答案最接近我正在寻找的答案。
添加了
enum.Enum
处理,因为这会导致RecursionError:超出最大递归深度
错误,并使用__slots__
重新排序对象,使其优先于定义 <代码>__dict__。Looked at all solutions, and @hbristow's answer was closest to what I was looking for.
Added
enum.Enum
handling since this was causing aRecursionError: maximum recursion depth exceeded
error and reordered objects with__slots__
to have precedence of objects defining__dict__
.我会对已接受的答案发表评论,但我的代表不够高......
接受的答案很好,但在
if
之后添加另一个elif
来支持 NamedTuples 序列化以正确地进行听写:I'd comment on the accepted answer but my rep is not high enough...
The accepted answer is great but add another
elif
just after theif
to support NamedTuples serialization to dict properly too:出色地。 添加了限制 @Shabbyrobe 答案深度的功能。 认为对于回环的对象来说可能是值得的。
Well. Added functionality of limiting the depth to @Shabbyrobe answer. Thought it might be worth for the objects which loop back.
当类字段是类实例时,前面的答案不起作用。 用这个:
previous answers not work when class field is class instance. use this: