Skip to content

Instantly share code, notes, and snippets.

@jomido
Created November 3, 2016 15:28
Show Gist options
  • Save jomido/c6faf65152e9ad78950305a94e92924a to your computer and use it in GitHub Desktop.
Save jomido/c6faf65152e9ad78950305a94e92924a to your computer and use it in GitHub Desktop.
Auto create classes from arbitrarily nested dicts, preserving list context
from collections import defaultdict
class AutoClass(object):
pass
def inflate(data, class_from_fields):
"""
Given arbitarily nested :data limited to dict and list container types,
and a mapping of fields -> class name, automatically generate classes from
dicts, preserving any list context from :data.
Return this as :objects, along with :instances. :instances is a dictionary
mapping class names to instances.
"""
def __init__(self, kwargs):
self.kwargs = kwargs
self.fields = make_key(kwargs)
self.__dict__.update(kwargs)
def __getitem__(self, name):
return self.__dict__[name]
def __setitem__(self, name, value):
self.__dict__[name] = value
class_definition = {
'__init__': __init__,
'__getitem__': __getitem__,
'__setitem__': __setitem__,
}
make_key = lambda d: tuple(sorted([str(k) for k in d.keys()]))
make_class = lambda key: type(
class_from_fields[key],
(AutoClass,),
class_definition
)
registries = {'classes': {}, 'instances': defaultdict(list)}
def inflate(data):
if isinstance(data, list):
return [inflate(elem) for elem in data]
elif isinstance(data, dict):
key = make_key(data)
try:
name = class_from_fields[key]
except KeyError:
msg = "Please add a class name entry for the following: {}"
raise Exception(msg.format(
key
))
cls = registries['classes'].get(name) or make_class(key)
registries['classes'][name] = cls
kwargs = {k: inflate(v) for k, v in data.items()}
instance = cls(kwargs)
registries['instances'][name].append(instance)
return instance
else:
return data
return inflate(data), registries['instances']
indent = 2
def print_walk(objects, indent=4):
def print_walk(objects, depth=0, name=''):
tab = depth * ' ' * indent
if isinstance(objects, list):
if objects:
print('{}['.format(tab))
[print_walk(o, depth + 1) for o in objects]
print('{}]'.format(tab))
else:
print('{}[]'.format(tab))
elif isinstance(objects, AutoClass):
if objects.fields:
print('{}{} {{'.format(tab, objects.__class__.__name__))
[print_walk(getattr(objects, f), depth + 1, name=f)
for f in objects.fields]
print('{}}}'.format(tab))
else:
print('{}{{}}'.format(tab))
else:
if name:
print('{}{}: {}'.format(tab, name, str(objects)))
else:
print('{}{}'.format(tab, str(objects)))
print_walk(objects)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment