Phillip B Oldham Phillip B Oldham - 1 month ago 10
Python Question

Pythonic way to "flatten" object hierarchy to nested dicts?

I need to "flatten" objects into nested dicts of the object's properties. The objects I want to do this with are generally just containers for basic types or other objects which act in a similar way. For example:

class foo(object):
bar = None
baz = None

class spam(object):
eggs = []

x = spam()
y = foo()
y.bar = True
y.baz = u"boz"
x.eggs.append(y)


What I need to "flatten" this to is:

{ 'eggs': [ { 'bar': True, 'baz': u'boz' } ] }


Is there anything in the stdlib which can do this for me? If not, would I have to test
isinstance
against all known base-types to ensure I don't try to convert an object which can't be converted (eg: bool)?

Edit:

These are objects are being returned to my code from an external library and therefore I have no control over them. I could use them as-is in my methods, but it would be easier (safer?) to convert them to dicts - especially for unit testing.

van van
Answer

Code: You may need to handle other iterable types though:

def flatten(obj):
    if obj is None:
        return None
    elif hasattr(obj, '__dict__') and obj.__dict__:
        return dict([(k, flatten(v)) for (k, v) in obj.__dict__.items()])
    elif isinstance(obj, (dict,)):
        return dict([(k, flatten(v)) for (k, v) in obj.items()])
    elif isinstance(obj, (list,)):
        return [flatten(x) for x in obj]
    elif isinstance(obj, (tuple,)):
        return tuple([flatten(x) for x in obj])
    else:
        return obj

Bug? In your code instead of:

class spam(object):
    eggs = []

x = spam()
x.eggs.add(...)

please do:

class spam(object):
    eggs = None #// if you need this line at all though

x = spam()
x.eggs = []
x.eggs.add(...)

If you do not, then all instances of spam will share the same eggs list.