Skip to content

Access Python dict using dot notation

Recently, an addition to Python 3.12 might be reverted in cpython#105948, where a new json.AttrDict class could enable accessing a dict using dot notation using a json.load hook.

But as pointed out in a related issue thread, this is already possible using the standard library's SimpleNamespace.

This was the proposed use case from the original PR which introduced the AttrDict class:

import json


class AttrDict(dict):
    """Dict like object that supports attribute style dotted access.
    This class is intended for use with the *object_hook* in json.loads():
        >>> from json import loads, AttrDict
        >>> json_string = '{"mercury": 88, "venus": 225, "earth": 365, "mars": 687}'
        >>> orbital_period = loads(json_string, object_hook=AttrDict)
        >>> orbital_period['earth']     # Dict style lookup
        365
        >>> orbital_period.earth        # Attribute style lookup
        365
        >>> orbital_period.keys()       # All dict methods are present
        dict_keys(['mercury', 'venus', 'earth', 'mars'])
    Attribute style access only works for keys that are valid attribute names.
    In contrast, dictionary style access works for all keys.
    For example, ``d.two words`` contains a space and is not syntactically
    valid Python, so ``d["two words"]`` should be used instead.
    If a key has the same name as dictionary method, then a dictionary
    lookup finds the key and an attribute lookup finds the method:
        >>> d = AttrDict(items=50)
        >>> d['items']                  # Lookup the key
        50
        >>> d.items()                   # Call the method
        dict_items([('items', 50)])
    """
    __slots__ = ()

    def __getattr__(self, attr):
        try:
            return self[attr]
        except KeyError:
            raise AttributeError(attr) from None

    def __setattr__(self, attr, value):
        self[attr] = value

    def __delattr__(self, attr):
        try:
            del self[attr]
        except KeyError:
            raise AttributeError(attr) from None

    def __dir__(self):
        return list(self) + dir(type(self))


with open('kepler.json') as f:
    kepler = json.load(f, object_hook=AttrDict)
print(kepler.orbital_period.neptune)

And this is how you can already achieve it:

>>> import json
>>> from types import SimpleNamespace
>>> data = '{"foo": {"bar": "val"}}'
>>> obj = json.loads(data, object_hook=lambda x: SimpleNamespace(**x))
>>> obj.foo.bar
'val'

Warning

However, be warned of what happens when there is no key:

>>> obj.baz
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'types.SimpleNamespace' object has no attribute 'baz'

Or, when you don't go far enough:

>>> obj
namespace(foo=namespace(bar='val'))

Alternative solutions

Alternative libraries that might be worth checking out if you want more advanced behavior:

Here's also a Hacker News thread on this topic: https://news.ycombinator.com/item?id=36977606

Comments