単純なオブジェクトグラフから辞書にデータを変換しようとしています。タイプ情報やメソッドは必要ありません。また、オブジェクトに変換し直す必要もありません。
オブジェクトのフィールドから辞書を作成することに関するこの質問 を見つけましたが、再帰的には実行しません。
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
これはうまく機能しているようで、例外は必要ありませんが、ここでケースがあるかどうかはまだわかりませんが、どこに落ちるのかわかりません。
任意の提案をいただければ幸いです。
私自身の試みと、AnuragUniyalとLennartRegebroの回答から導き出された手がかりの融合は、私にとって最も効果的です。
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
オブジェクトをJSONに再帰的に変換する1行のコード。
import json
def get_json(object):
return json.loads(
json.dumps(object, default=lambda o: getattr(o, '__dict__', str(o)))
)
object = SomeClass()
print("Json = ", get_json(object))
ベースストリングまたはオブジェクトをチェックする目的が何であるかわかりませんか?また、dictは、そのような呼び出し可能オブジェクトを指す属性がない限り、呼び出し可能オブジェクトを含みませんが、その場合、そのオブジェクトの一部ではありませんか?
したがって、さまざまなタイプと値をチェックする代わりに、todictでオブジェクトを変換し、例外が発生した場合は、元の値を使用します。
todictは、objに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}}考慮すべき他のケースがあるかもしれませんが、それは良いスタートかもしれません
特殊なケースオブジェクトがスロットを使用している場合、取得できませんdict例:.
class A(object):
__slots__ = ["a1"]
def __init__(self):
self.a1 = 1
スロットの場合の修正は、dictを直接使用する代わりにdir()を使用することです。
この答えは数年遅すぎることに気づきましたが、@ Shabbyrobeによる元のソリューションに対するPython 3.3+互換性のある変更であり、一般的にうまく機能しているので、共有する価値があるかもしれないと思いました私:
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))
Pythonでは、メタクラスなど、オブジェクトの動作をわずかに異なるものにする方法はたくさんあり、getattrをオーバーライドできるため、「魔法の」属性を使用できません。シースルーdictなど。要するに、どの方法を使用しても、一般的なケースで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をオーバーライドした場合、これはまだカバーされていません。カバーされておらず、カバーできない可能性があるケースは他にもたくさんあると思います。 :)
これを行うには時間がかかりますが簡単な方法は、jsonpickle
を使用してオブジェクトをJSON文字列に変換し、次に_json.loads
_を使用してオブジェクトをpython辞書:
dict = json.loads(jsonpickle.encode( obj, unpicklable=False ))
namedtuple
sで機能するように、Shabbyrobeの回答を少し更新しました。
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
すべての解決策を調べたところ、@ hbristowの答えは私が探していたものに最も近いものでした。 enum.Enum
エラーが発生し、RecursionError: maximum recursion depth exceeded
でオブジェクトを並べ替えて、__slots__
を定義するオブジェクトよりも優先されるため、__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
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