Pythonic way to convert a dictionary into namedtuple or another hashable dict-like?

I have a dictionary like:

d = {'a': 1, 'b': 2, 'c': 3, 'd': 4}

which I would like to convert to a namedtuple. My current approach is with the following code

namedTupleConstructor = namedtuple('myNamedTuple', ' '.join(sorted(d.keys())))
nt= namedTupleConstructor(**d)

which produces

myNamedTuple(a=1, b=2, c=3, d=4)

This works fine for me (I think), but am I missing a built-in such as...

nt = namedtuple.from_dict() ?

UPDATE: as discussed in the comments, my reason for wanting to convert my dictionary to a namedtuple is so that it becomes hashable, but still generally useable like a dict.

UPDATE2: 4 years after I've posted this question, TLK posts a new answer recommending using the dataclass decorator that I think is really great. I think that's now what I would use going forward.


Solution 1:

To create the subclass, you may just pass the keys of a dict directly:

MyTuple = namedtuple('MyTuple', d)

Now to create tuple instances from this dict, or any other dict with matching keys:

my_tuple = MyTuple(**d)

Beware: namedtuples compare on values only (ordered). They are designed to be a drop-in replacement for regular tuples, with named attribute access as an added feature. The field names will not be considered when making equality comparisons. It may not be what you wanted nor expected from the namedtuple type! This differs from dict equality comparisons, which do take into account the keys and also compare order agnostic.

For readers who don't really need a type which is a subclass of tuple, there probably isn't much point to use a namedtuple in the first place. If you just want to use attribute access syntax on fields, it would be simpler and easier to create namespace objects instead:

>>> from types import SimpleNamespace
>>> SimpleNamespace(**d)
namespace(a=1, b=2, c=3, d=4)

my reason for wanting to convert my dictionary to a namedtuple is so that it becomes hashable, but still generally useable like a dict

For a hashable "attrdict" like recipe, check out a frozen box:

>>> from box import Box
>>> b = Box(d, frozen_box=True)
>>> hash(b)
7686694140185755210
>>> b.a
1
>>> b["a"]
1
>>> b["a"] = 2
BoxError: Box is frozen

There may also be a frozen mapping type coming in a later version of Python, watch this draft PEP for acceptance or rejection:

PEP 603 -- Adding a frozenmap type to collections

Solution 2:

from collections import namedtuple
nt = namedtuple('x', d.keys())(*d.values())