Skip to content

Instantly share code, notes, and snippets.

@href
Created October 27, 2011 12:00
Show Gist options
  • Save href/1319371 to your computer and use it in GitHub Desktop.
Save href/1319371 to your computer and use it in GitHub Desktop.
Convert any dictionary to a named tuple
from collections import namedtuple
def convert(dictionary):
return namedtuple('GenericDict', dictionary.keys())(**dictionary)
"""
>>> d = dictionary(a=1, b='b', c=[3])
>>> named = convert(d)
>>> named.a == d.a
True
>>> named.b == d.b
True
>>> named.c == d.c
True
"""
@JinghongHuang
Copy link

Above code breaks if the item of the list is not a dictionary. Modifying the code as following, which should work for most of the cases:

from collections import namedtuple
def convert(obj):
    if isinstance(obj, dict):
        for key, value in obj.iteritems():
            obj[key] = convert(value) 
        return namedtuple('GenericDict', obj.keys())(**obj)
    elif isinstance(obj, list):
        return [convert(item) for item in obj]
    else:
        return obj

@liiight
Copy link

liiight commented Oct 25, 2017

Thanks for this!

@junjizhi
Copy link

junjizhi commented Aug 2, 2018

@JinghongHuang The snippet you posted above has bad performance because it creates many GenericDict classes recursively and in python these classes are never garbage recycled so it will memory leaks!

@haydenflinner
Copy link

Anyone interested in this should also check out the dotmap package / alternatives to that package

@malcolmgreaves
Copy link

malcolmgreaves commented Dec 11, 2018

I wrote up this [1] implementation a little while ago. Stumbled upon this thread. From the namedtuple_fmt module, the serialize function accepts a dictionary (e.g. from a json.load call) and will convert to an instance of the given namedtuple-deriving type. Likewise, deserialize will convert any namedtuple-deriving type into a dictionary-l object. These functions also work on any list-like type: List and Tuple are process-able.

E.g.

import json
from typing import Sequence

from namedtuple_fmt import serialize, deserialize

X = NamedTuple('X', [('msg',str)])

json_str="""{"msg": "This is the first message"}"""
first_msg = deserialize(json.loads(json_str), X) 
print(first_msg.msg)
print(deserialize(serialize(first_msg)) == X("This is the first message"))
print(deserialize(json.loads(json.dumps(serialize(first_msg)))) == X("This is the first message"))

json_str="""[{"msg": "This is the first message"},{"msg": "This is the 2nd message"}]"""
messages = deserialize(json.loads(json_str), Sequence[X])
print(f"{len(messages)} messages")
print('\n'.join(map(lambda x: x.msg, messages))

Implementation note: There are explicit type checks for the Sequence types. It's important to not mess-up when it comes to handling str and tuple. A first draft of this idea incorrectly did for _ in X when trying to "test" if something was list-like. This idea, unfortunately, will iterate over characters in a str or elements in a tuple. We want the tuple-iterating part, but not when it comes to a NamedTuple (or namedtuple)!

[1] https://gist.github.com/malcolmgreaves/d71ae1f09075812e54d8ec54a5613616

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment