Created
September 22, 2012 04:49
-
-
Save caruccio/3765157 to your computer and use it in GitHub Desktop.
metattuple - recursively creating a namedtuple class from a dict object using a metaclass
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
''' metatuple - Recursive namedtuple from arbitrary dict | |
After 2 hours of intensive coding and some tequila sips I found | |
a "simple" solution to create a namedtuple from any dictionary, | |
recursivelly creating any necessary namedtuple. | |
Probably there are tons of easiest ways of doing that, like some | |
well documented method or standart function in the python library, | |
but that wouldn't be fun.''' | |
from collections import namedtuple | |
def metatuple(name, attrs): | |
'''Creates a namedtuple from an arbitrary dict, | |
recursivelly namedtuple()ing any dict it finds on the way. | |
>>> person = { | |
... 'name': 'Charlotte', | |
... 'address': { | |
... 'street': 'Acacia Avenue', | |
... 'number': 22, | |
... }, | |
... } | |
>>> Person = metatuple('Person', person) | |
>>> charlotte = Person(**person) | |
>>> assert charlotte.name == 'Charlotte' | |
>>> assert charlotte.address.street == 'Acacia Avenue' | |
>>> assert charlotte.address.number == 22 | |
# ensure dict's random key ordering don't affect correctness | |
>>> rand_dict = { | |
... 'a_field0': 0, | |
... 'b_field1': 1, | |
... 'c_field2': 2, | |
... 'd_field3': 3, | |
... 'e_field4': 4, | |
... 'f_field5': 5, | |
... 'g_field6': 6, | |
... } | |
>>> RandDict = metatuple('RandDict', rand_dict) | |
>>> rand = RandDict(**rand_dict) | |
>>> assert rand.a_field0 == 0 | |
>>> assert rand.b_field1 == 1 | |
>>> assert rand.c_field2 == 2 | |
>>> assert rand.d_field3 == 3 | |
>>> assert rand.e_field4 == 4 | |
>>> assert rand.f_field5 == 5 | |
>>> assert rand.g_field6 == 6 | |
''' | |
class _meta_cls(type): | |
'''Metaclass to replace namedtuple().__new__ with a recursive version _meta_new().''' | |
def __new__(mcs, name, bases, metadict): | |
def _meta_new(_cls, **kv): | |
return tuple.__new__(_cls, ([ (metatuple('%s_%s' % (_cls.__name__, k), kv[k].keys())(**kv[k]) if isinstance(kv[k], dict) else kv[k]) for k in _cls._fields])) | |
metadict['__new__'] = _meta_new | |
return type.__new__(mcs, bases[0].__name__, bases, metadict) | |
class _metabase(namedtuple(name, ' '.join(attrs))): | |
'''Wrapper metaclass for namedtuple''' | |
__metaclass__ = _meta_cls | |
return _metabase | |
if __name__ == "__main__": | |
import doctest | |
doctest.testmod() |
Fixed, but with some performance penalty.
Fix: no need to sort dict anymore
This is very interesting!
I was trying to accomplish a similar end-goal to you but wanted to avoid using metaclasses (for simpler code); this is what I came up with: https://gist.github.com/hangtwenty/5960435
Cheers
@hangtwenty Funny is that I can't even understand my code anymore :)
Your is much more readable, plus it supports lists.
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Beware: It is not working as expected.
When instantiating an object, values are being assigned with different order as those defined on class definition.
Need to fix this.