How to use a dot "." to access members of dictionary?

PythonDictionarySyntaxNested

Python Problem Overview


How do I make Python dictionary members accessible via a dot "."?

For example, instead of writing mydict['val'], I'd like to write mydict.val.

Also I'd like to access nested dicts this way. For example

mydict.mydict2.val 

would refer to

mydict = { 'mydict2': { 'val': ... } }

Python Solutions


Solution 1 - Python

I've always kept this around in a util file. You can use it as a mixin on your own classes too.

class dotdict(dict):
    """dot.notation access to dictionary attributes"""
    __getattr__ = dict.get
    __setattr__ = dict.__setitem__
    __delattr__ = dict.__delitem__
    
mydict = {'val':'it works'}
nested_dict = {'val':'nested works too'}
mydict = dotdict(mydict)
mydict.val
# 'it works'

mydict.nested = dotdict(nested_dict)
mydict.nested.val
# 'nested works too'

Solution 2 - Python

You can do it using this class I just made. With this class you can use the Map object like another dictionary(including json serialization) or with the dot notation. I hope to help you:

class Map(dict):
    """
    Example:
    m = Map({'first_name': 'Eduardo'}, last_name='Pool', age=24, sports=['Soccer'])
    """
    def __init__(self, *args, **kwargs):
        super(Map, self).__init__(*args, **kwargs)
        for arg in args:
            if isinstance(arg, dict):
                for k, v in arg.iteritems():
                    self[k] = v

        if kwargs:
            for k, v in kwargs.iteritems():
                self[k] = v

    def __getattr__(self, attr):
        return self.get(attr)

    def __setattr__(self, key, value):
        self.__setitem__(key, value)

    def __setitem__(self, key, value):
        super(Map, self).__setitem__(key, value)
        self.__dict__.update({key: value})

    def __delattr__(self, item):
        self.__delitem__(item)

    def __delitem__(self, key):
        super(Map, self).__delitem__(key)
        del self.__dict__[key]

Usage examples:

m = Map({'first_name': 'Eduardo'}, last_name='Pool', age=24, sports=['Soccer'])
# Add new key
m.new_key = 'Hello world!'
# Or
m['new_key'] = 'Hello world!'
print m.new_key
print m['new_key']
# Update values
m.new_key = 'Yay!'
# Or
m['new_key'] = 'Yay!'
# Delete key
del m.new_key
# Or
del m['new_key']

Solution 3 - Python

Install dotmap via pip

pip install dotmap

It does everything you want it to do and subclasses dict, so it operates like a normal dictionary:

from dotmap import DotMap

m = DotMap()
m.hello = 'world'
m.hello
m.hello += '!'
# m.hello and m['hello'] now both return 'world!'
m.val = 5
m.val2 = 'Sam'

On top of that, you can convert it to and from dict objects:

d = m.toDict()
m = DotMap(d) # automatic conversion in constructor

This means that if something you want to access is already in dict form, you can turn it into a DotMap for easy access:

import json
jsonDict = json.loads(text)
data = DotMap(jsonDict)
print data.location.city

Finally, it automatically creates new child DotMap instances so you can do things like this:

m = DotMap()
m.people.steve.age = 31

Comparison to Bunch

Full disclosure: I am the creator of the DotMap. I created it because Bunch was missing these features

  • remembering the order items are added and iterating in that order
  • automatic child DotMap creation, which saves time and makes for cleaner code when you have a lot of hierarchy
  • constructing from a dict and recursively converting all child dict instances to DotMap

Solution 4 - Python

Derive from dict and and implement __getattr__ and __setattr__.

Or you can use Bunch which is very similar.

I don't think it's possible to monkeypatch built-in dict class.

Solution 5 - Python

Use SimpleNamespace:

>>> from types import SimpleNamespace   
>>> d = dict(x=[1, 2], y=['a', 'b'])
>>> ns = SimpleNamespace(**d)
>>> ns.x
[1, 2]
>>> ns
namespace(x=[1, 2], y=['a', 'b'])

Solution 6 - Python

Fabric has a really nice, minimal implementation. Extending that to allow for nested access, we can use a defaultdict, and the result looks something like this:

from collections import defaultdict

class AttributeDict(defaultdict):
    def __init__(self):
        super(AttributeDict, self).__init__(AttributeDict)

    def __getattr__(self, key):
        try:
            return self[key]
        except KeyError:
            raise AttributeError(key)

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

Make use of it as follows:

keys = AttributeDict()
keys.abc.xyz.x = 123
keys.abc.xyz.a.b.c = 234

That elaborates a bit on Kugel's answer of "Derive from dict and and implement __getattr__ and __setattr__". Now you know how!

Solution 7 - Python

I tried this:

class dotdict(dict):
    def __getattr__(self, name):
	    return self[name]

you can try __getattribute__ too.

make every dict a type of dotdict would be good enough, if you want to init this from a multi-layer dict, try implement __init__ too.

Solution 8 - Python

I recently came across the 'Box' library which does the same thing.

Installation command : pip install python-box

Example:

from box import Box

mydict = {"key1":{"v1":0.375,
                    "v2":0.625},
          "key2":0.125,
          }
mydict = Box(mydict)

print(mydict.key1.v1)

I found it to be more effective than other existing libraries like dotmap, which generate python recursion error when you have large nested dicts.

link to library and details: https://pypi.org/project/python-box/

Solution 9 - Python

If you want to pickle your modified dictionary, you need to add few state methods to above answers:

class DotDict(dict):
    """dot.notation access to dictionary attributes"""
    def __getattr__(self, attr):
        return self.get(attr)
    __setattr__= dict.__setitem__
    __delattr__= dict.__delitem__

    def __getstate__(self):
        return self

    def __setstate__(self, state):
        self.update(state)
        self.__dict__ = self

Solution 10 - Python

Don't. Attribute access and indexing are separate things in Python, and you shouldn't want them to perform the same. Make a class (possibly one made by namedtuple) if you have something that should have accessible attributes and use [] notation to get an item from a dict.

Solution 11 - Python

Building on Kugel's answer and taking Mike Graham's words of caution into consideration, what if we make a wrapper?

class DictWrap(object):
  """ Wrap an existing dict, or create a new one, and access with either dot 
    notation or key lookup.

    The attribute _data is reserved and stores the underlying dictionary.
    When using the += operator with create=True, the empty nested dict is 
    replaced with the operand, effectively creating a default dictionary
    of mixed types.

    args:
      d({}): Existing dict to wrap, an empty dict is created by default
      create(True): Create an empty, nested dict instead of raising a KeyError
    
    example:
      >>>dw = DictWrap({'pp':3})
      >>>dw.a.b += 2
      >>>dw.a.b += 2
      >>>dw.a['c'] += 'Hello'
      >>>dw.a['c'] += ' World'
      >>>dw.a.d
      >>>print dw._data
      {'a': {'c': 'Hello World', 'b': 4, 'd': {}}, 'pp': 3}
      
  """

  def __init__(self, d=None, create=True):
    if d is None:
      d = {}
    supr = super(DictWrap, self)  
    supr.__setattr__('_data', d)
    supr.__setattr__('__create', create)

  def __getattr__(self, name):
    try:
      value = self._data[name]
    except KeyError:
      if not super(DictWrap, self).__getattribute__('__create'):
        raise
      value = {}
      self._data[name] = value

    if hasattr(value, 'items'):
      create = super(DictWrap, self).__getattribute__('__create')
      return DictWrap(value, create)
    return value
  
  def __setattr__(self, name, value):
    self._data[name] = value  
    
  def __getitem__(self, key):
    try:
      value = self._data[key]
    except KeyError:
      if not super(DictWrap, self).__getattribute__('__create'):
        raise
      value = {}
      self._data[key] = value

    if hasattr(value, 'items'):
      create = super(DictWrap, self).__getattribute__('__create')
      return DictWrap(value, create)
    return value
  
  def __setitem__(self, key, value):
    self._data[key] = value
  
  def __iadd__(self, other):
    if self._data:
      raise TypeError("A Nested dict will only be replaced if it's empty")
    else:
      return other

Solution 12 - Python

To build upon epool's answer, this version allows you to access any dict inside via the dot operator:

foo = {
    "bar" : {
        "baz" : [ {"boo" : "hoo"} , {"baba" : "loo"} ]
    }
}

For instance, foo.bar.baz[1].baba returns "loo".

class Map(dict):
    def __init__(self, *args, **kwargs):
        super(Map, self).__init__(*args, **kwargs)
        for arg in args:
            if isinstance(arg, dict):
                for k, v in arg.items():
                    if isinstance(v, dict):
                        v = Map(v)
                    if isinstance(v, list):
                        self.__convert(v)
                    self[k] = v

        if kwargs:
            for k, v in kwargs.items():
                if isinstance(v, dict):
                    v = Map(v)
                elif isinstance(v, list):
                    self.__convert(v)
                self[k] = v

    def __convert(self, v):
        for elem in range(0, len(v)):
            if isinstance(v[elem], dict):
                v[elem] = Map(v[elem])
            elif isinstance(v[elem], list):
                self.__convert(v[elem])

    def __getattr__(self, attr):
        return self.get(attr)

    def __setattr__(self, key, value):
        self.__setitem__(key, value)

    def __setitem__(self, key, value):
        super(Map, self).__setitem__(key, value)
        self.__dict__.update({key: value})

    def __delattr__(self, item):
        self.__delitem__(item)

    def __delitem__(self, key):
        super(Map, self).__delitem__(key)
        del self.__dict__[key]

Solution 13 - Python

Use __getattr__, very simple, works in Python 3.4.3

class myDict(dict):
    def __getattr__(self,val):
        return self[val]


blockBody=myDict()
blockBody['item1']=10000
blockBody['item2']="StackOverflow"
print(blockBody.item1)
print(blockBody.item2)

Output:

10000
StackOverflow

Solution 14 - Python

I like the Munch and it gives lot of handy options on top of dot access.

> import munch > > temp_1 = {'person': { 'fname': 'senthil', 'lname': 'ramalingam'}} > > dict_munch = munch.munchify(temp_1) > > dict_munch.person.fname

Solution 15 - Python

You can achieve this using SimpleNamespace

from types import SimpleNamespace
# Assign values
args = SimpleNamespace()
args.username = 'admin'

# Retrive values
print(args.username)  # output: admin

Solution 16 - Python

The language itself doesn't support this, but sometimes this is still a useful requirement. Besides the Bunch recipe, you can also write a little method which can access a dictionary using a dotted string:

def get_var(input_dict, accessor_string):
    """Gets data from a dictionary using a dotted accessor-string"""
    current_data = input_dict
    for chunk in accessor_string.split('.'):
        current_data = current_data.get(chunk, {})
    return current_data

which would support something like this:

>> test_dict = {'thing': {'spam': 12, 'foo': {'cheeze': 'bar'}}}
>> output = get_var(test_dict, 'thing.spam.foo.cheeze')
>> print output
'bar'
>>

Solution 17 - Python

I ended up trying BOTH the AttrDict and the Bunch libraries and found them to be way to slow for my uses. After a friend and I looked into it, we found that the main method for writing these libraries results in the library aggressively recursing through a nested object and making copies of the dictionary object throughout. With this in mind, we made two key changes. 1) We made attributes lazy-loaded 2) instead of creating copies of a dictionary object, we create copies of a light-weight proxy object. This is the final implementation. The performance increase of using this code is incredible. When using AttrDict or Bunch, these two libraries alone consumed 1/2 and 1/3 respectively of my request time(what!?). This code reduced that time to almost nothing(somewhere in the range of 0.5ms). This of course depends on your needs, but if you are using this functionality quite a bit in your code, definitely go with something simple like this.

class DictProxy(object):
    def __init__(self, obj):
        self.obj = obj
        
    def __getitem__(self, key):
        return wrap(self.obj[key])
        
    def __getattr__(self, key):
        try:
            return wrap(getattr(self.obj, key))
        except AttributeError:
            try:
                return self[key]
            except KeyError:
                raise AttributeError(key)
        
    # you probably also want to proxy important list properties along like
    # items(), iteritems() and __len__
            
class ListProxy(object):
    def __init__(self, obj):
        self.obj = obj
        
    def __getitem__(self, key):
        return wrap(self.obj[key])
        
    # you probably also want to proxy important list properties along like
    # __iter__ and __len__
        
def wrap(value):
    if isinstance(value, dict):
        return DictProxy(value)
    if isinstance(value, (tuple, list)):
        return ListProxy(value)
    return value

See the original implementation here by https://stackoverflow.com/users/704327/michael-merickel.

The other thing to note, is that this implementation is pretty simple and doesn't implement all of the methods you might need. You'll need to write those as required on the DictProxy or ListProxy objects.

Solution 18 - Python

This solution is a refinement upon the one offered by epool to address the requirement of the OP to access nested dicts in a consistent manner. The solution by epool did not allow for accessing nested dicts.

class YAMLobj(dict):
    def __init__(self, args):
        super(YAMLobj, self).__init__(args)
        if isinstance(args, dict):
            for k, v in args.iteritems():
                if not isinstance(v, dict):
                    self[k] = v
                else:
                    self.__setattr__(k, YAMLobj(v))


    def __getattr__(self, attr):
        return self.get(attr)

    def __setattr__(self, key, value):
        self.__setitem__(key, value)

    def __setitem__(self, key, value):
        super(YAMLobj, self).__setitem__(key, value)
        self.__dict__.update({key: value})

    def __delattr__(self, item):
        self.__delitem__(item)

    def __delitem__(self, key):
        super(YAMLobj, self).__delitem__(key)
        del self.__dict__[key]

With this class, one can now do something like: A.B.C.D.

Solution 19 - Python

def dict_to_object(dick):
    # http://stackoverflow.com/a/1305663/968442

    class Struct:
        def __init__(self, **entries):
            self.__dict__.update(entries)

    return Struct(**dick)

If one decides to permanently convert that dict to object this should do. You can create a throwaway object just before accessing.

d = dict_to_object(d)

Solution 20 - Python

Using namedtuple allows dot access.

It is like a lightweight object which also has the properties of a tuple.

It allows to define properties and access them using the dot operator.

from collections import namedtuple
Data = namedtuple('Data', ['key1', 'key2'])

dataObj = Data(val1, key2=val2) # can instantiate using keyword arguments and positional arguments

Access using dot operator

dataObj.key1 # Gives val1
datObj.key2 # Gives val2

Access using tuple indices

dataObj[0] # Gives val1
dataObj[1] # Gives val2

But remember this is a tuple; not a dict. So the below code will give error

dataObj['key1'] # Gives TypeError: tuple indices must be integers or slices, not str

Refer: namedtuple

Solution 21 - Python

It is an old question but I recently found that sklearn has an implemented version dict accessible by key, namely Bunch https://scikit-learn.org/stable/modules/generated/sklearn.utils.Bunch.html#sklearn.utils.Bunch

Solution 22 - Python

For infinite levels of nesting of dicts, lists, lists of dicts, and dicts of lists.

It also supports pickling

This is an extension of this answer.

class DotDict(dict):
    # https://stackoverflow.com/a/70665030/913098
    """
    Example:
    m = Map({'first_name': 'Eduardo'}, last_name='Pool', age=24, sports=['Soccer'])

    Iterable are assumed to have a constructor taking list as input.
    """

    def __init__(self, *args, **kwargs):
        super(DotDict, self).__init__(*args, **kwargs)

        args_with_kwargs = []
        for arg in args:
            args_with_kwargs.append(arg)
        args_with_kwargs.append(kwargs)
        args = args_with_kwargs

        for arg in args:
            if isinstance(arg, dict):
                for k, v in arg.items():
                    self[k] = v
                    if isinstance(v, dict):
                        self[k] = DotDict(v)
                    elif isinstance(v, str) or isinstance(v, bytes):
                        self[k] = v
                    elif isinstance(v, Iterable):
                        klass = type(v)
                        map_value: List[Any] = []
                        for e in v:
                            map_e = DotDict(e) if isinstance(e, dict) else e
                            map_value.append(map_e)
                        self[k] = klass(map_value)



    def __getattr__(self, attr):
        return self.get(attr)

    def __setattr__(self, key, value):
        self.__setitem__(key, value)

    def __setitem__(self, key, value):
        super(DotDict, self).__setitem__(key, value)
        self.__dict__.update({key: value})

    def __delattr__(self, item):
        self.__delitem__(item)

    def __delitem__(self, key):
        super(DotDict, self).__delitem__(key)
        del self.__dict__[key]

    def __getstate__(self):
        return self.__dict__

    def __setstate__(self, d):
        self.__dict__.update(d)


if __name__ == "__main__":
    import pickle
    def test_map():
        d = {
            "a": 1,
            "b": {
                "c": "d",
                "e": 2,
                "f": None
            },
            "g": [],
            "h": [1, "i"],
            "j": [1, "k", {}],
            "l":
                [
                    1,
                    "m",
                    {
                        "n": [3],
                        "o": "p",
                        "q": {
                            "r": "s",
                            "t": ["u", 5, {"v": "w"}, ],
                            "x": ("z", 1)
                        }
                    }
                ],
        }
        map_d = DotDict(d)
        w = map_d.l[2].q.t[2].v
        assert w == "w"

        pickled = pickle.dumps(map_d)
        unpickled = pickle.loads(pickled)
        assert unpickled == map_d

        kwargs_check = DotDict(a=1, b=[dict(c=2, d="3"), 5])
        assert kwargs_check.b[0].d == "3"

        kwargs_and_args_check = DotDict(d, a=1, b=[dict(c=2, d="3"), 5])
        assert kwargs_and_args_check.l[2].q.t[2].v == "w"
        assert kwargs_and_args_check.b[0].d == "3"



    test_map()

Solution 23 - Python

One simple way to get dot access (but not array access), is to use a plain object in Python. Like this:

class YourObject:
    def __init__(self, *args, **kwargs):
        for k, v in kwargs.items():
            setattr(self, k, v)

...and use it like this:

>>> obj = YourObject(key="value")
>>> print(obj.key)
"value"

... to convert it to a dict:

>>> print(obj.__dict__)
{"key": "value"}

Solution 24 - Python

The answer of @derek73 is very neat, but it cannot be pickled nor (deep)copied, and it returns None for missing keys. The code below fixes this.

Edit: I did not see the answer above that addresses the exact same point (upvoted). I'm leaving the answer here for reference.

class dotdict(dict):
    __setattr__ = dict.__setitem__
    __delattr__ = dict.__delitem__

    def __getattr__(self, name):
        try:
            return self[name]
        except KeyError:
            raise AttributeError(name)

Solution 25 - Python

I just needed to access a dictionary using a dotted path string, so I came up with:

def get_value_from_path(dictionary, parts):
    """ extracts a value from a dictionary using a dotted path string """

    if type(parts) is str:
        parts = parts.split('.')

    if len(parts) > 1:
        return get_value_from_path(dictionary[parts[0]], parts[1:])

    return dictionary[parts[0]]

a = {'a':{'b':'c'}}
print(get_value_from_path(a, 'a.b')) # c

Solution 26 - Python

Not a direct answer to the OP's question, but inspired by and perhaps useful for some.. I've created an object-based solution using the internal __dict__ (In no way optimized code)

payload = {
    "name": "John",
    "location": {
        "lat": 53.12312312,
        "long": 43.21345112
    },
    "numbers": [
        {
            "role": "home",
            "number": "070-12345678"
        },
        {
            "role": "office",
            "number": "070-12345679"
        }
    ]
}


class Map(object):
    """
    Dot style access to object members, access raw values
    with an underscore e.g.
    
    class Foo(Map):
        def foo(self):
            return self.get('foo') + 'bar'
    
    obj = Foo(**{'foo': 'foo'})
    
    obj.foo => 'foobar'
    obj._foo => 'foo'
    
    """
    
    def __init__(self, *args, **kwargs):
        for arg in args:
            if isinstance(arg, dict):
                for k, v in arg.iteritems():
                    self.__dict__[k] = v
                    self.__dict__['_' + k] = v

        if kwargs:
            for k, v in kwargs.iteritems():
                self.__dict__[k] = v
                self.__dict__['_' + k] = v
        
    def __getattribute__(self, attr):
        if hasattr(self, 'get_' + attr):
            return object.__getattribute__(self, 'get_' + attr)()
        else:
            return object.__getattribute__(self, attr)
        
    def get(self, key):
        try:
            return self.__dict__.get('get_' + key)()
        except (AttributeError, TypeError):
            return self.__dict__.get(key)
            
    def __repr__(self):
        return u"<{name} object>".format(
            name=self.__class__.__name__
        )
        

class Number(Map):
    def get_role(self):
        return self.get('role')
        
    def get_number(self):
        return self.get('number')

        
class Location(Map):
    def get_latitude(self):
        return self.get('lat') + 1
        
    def get_longitude(self):
        return self.get('long') + 1
        

class Item(Map):
    def get_name(self):
        return self.get('name') + " Doe"
    
    def get_location(self):
        return Location(**self.get('location'))
        
    def get_numbers(self):
        return [Number(**n) for n in self.get('numbers')]


# Tests

obj = Item({'foo': 'bar'}, **payload)

assert type(obj) == Item
assert obj._name == "John"
assert obj.name == "John Doe"
assert type(obj.location) == Location
assert obj.location._lat == 53.12312312
assert obj.location._long == 43.21345112
assert obj.location.latitude == 54.12312312
assert obj.location.longitude == 44.21345112

for n in obj.numbers:
    assert type(n) == Number
    if n.role == 'home':
        assert n.number == "070-12345678"
    if n.role == 'office':
        assert n.number == "070-12345679"

Solution 27 - Python

This also works with nested dicts and makes sure that dicts which are appended later behave the same:

class DotDict(dict):

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        # Recursively turn nested dicts into DotDicts
        for key, value in self.items():
            if type(value) is dict:
                self[key] = DotDict(value)

    def __setitem__(self, key, item):
        if type(item) is dict:
            item = DotDict(item)
        super().__setitem__(key, item)

    __setattr__ = __setitem__
    __getattr__ = dict.__getitem__

Solution 28 - Python

Here's my version of @derek73 answer. I use dict.__getitem__ as __getattr__ so it still throws KeyError, and im renaming dict public methods with "" prefix (surrounded with "" leads to special methods name conflict, like __get__ which would be treated as a descriptor method). Anyway you can't get completely clear namespace for keys as attributes due to crucial dict base methods, so the solution isn't perfect but you can have keys - attributes like get, pop, items etc.

class DotDictMeta(type):                                                          
    def __new__(                                                                  
        cls,                                                                      
        name,                                                                     
        bases,                                                                    
        attrs,                                         
        rename_method=lambda n: f'__{n}__',                            
        **custom_methods,                                                         
    ):                                                                            
        d = dict                                                                  
        attrs.update(                                                             
            cls.get_hidden_or_renamed_methods(rename_method),           
            __getattr__=d.__getitem__,                                            
            __setattr__=d.__setitem__,                                            
            __delattr__=d.__delitem__,                                            
            **custom_methods,                                                     
        )                                                                         
        return super().__new__(cls, name, bases, attrs)                           
                                                                                  
    def __init__(self, name, bases, attrs, **_):                                  
        super().__init__(name, bases, attrs)                                      
                                                                                  
    @property                                                                     
    def attribute_error(self):                                                    
        raise AttributeError                                                      
                                                                                  
    @classmethod                                                                  
    def get_hidden_or_renamed_methods(cls, rename_method=None):                  
        public_methods = tuple(                                                   
            i for i in dict.__dict__.items() if not i[0].startswith('__')         
        )                                                                         
        error = cls.attribute_error                                               
        hidden_methods = ((k, error) for k, v in public_methods)                  
        yield from hidden_methods                                                 
        if rename_method:                                                       
            renamed_methods = ((rename_method(k), v) for k, v in public_methods) 
            yield from renamed_methods                                             
                                                                                  
                                                                                  
class DotDict(dict, metaclass=DotDictMeta):                                       
    pass  

                                                                    
                                                                              

You can remove dict methods from DotDict namespace and keep using dict class methods, its useful also when you want to operate on other dict instances and want to use the same methods without extra check whether its DotDict or not, eg.

dct = dict(a=1)
dot_dct = DotDict(b=2)
foo = {c: i for i, c in enumerate('xyz')}
for d in (dct, dot_dct):
    # you would have to use dct.update and dot_dct.__update methods
    dict.update(d, foo)
    
assert dict.get(dot, 'foo', 0) is 0

Solution 29 - Python

I just dug this up from a project I was working on a long time ago. It could probably be optimized a bit, but here it goes.

class DotNotation(dict):
    
    __setattr__= dict.__setitem__
    __delattr__= dict.__delitem__

    def __init__(self, data):
        if isinstance(data, str):
            data = json.loads(data)
    
        for name, value in data.items():
            setattr(self, name, self._wrap(value))

    def __getattr__(self, attr):
        def _traverse(obj, attr):
            if self._is_indexable(obj):
                try:
                    return obj[int(attr)]
                except:
                    return None
            elif isinstance(obj, dict):
                return obj.get(attr, None)
            else:
                return attr
        if '.' in attr:
            return reduce(_traverse, attr.split('.'), self)
        return self.get(attr, None)

    def _wrap(self, value):
        if self._is_indexable(value):
            # (!) recursive (!)
            return type(value)([self._wrap(v) for v in value])
        elif isinstance(value, dict):
            return DotNotation(value)
        else:
            return value
    
    @staticmethod
    def _is_indexable(obj):
        return isinstance(obj, (tuple, list, set, frozenset))


if __name__ == "__main__":
    test_dict = {
        "dimensions": {
            "length": "112",
            "width": "103",
            "height": "42"
        },
        "meta_data": [
            {
                "id": 11089769,
                "key": "imported_gallery_files",
                "value": [
                    "https://example.com/wp-content/uploads/2019/09/unnamed-3.jpg",
                    "https://example.com/wp-content/uploads/2019/09/unnamed-2.jpg",
                    "https://example.com/wp-content/uploads/2019/09/unnamed-4.jpg"
                ]
            }
        ]
    }
    dotted_dict = DotNotation(test_dict)
    print(dotted_dict.dimensions.length) # => '112'
    print(getattr(dotted_dict, 'dimensions.length')) # => '112'
    print(dotted_dict.meta_data[0].key) # => 'imported_gallery_files'
    print(getattr(dotted_dict, 'meta_data.0.key')) # => 'imported_gallery_files'
    print(dotted_dict.meta_data[0].value) # => ['link1','link2','link2']
    print(getattr(dotted_dict, 'meta_data.0.value')) # => ['link1','link2','link3']
    print(dotted_dict.meta_data[0].value[2]) # => 'link3'
    print(getattr(dotted_dict, 'meta_data.0.value.2')) # => 'link3'

Solution 30 - Python

My 2 cents: for my own purposes I developed minydra, a simple command-line parser which includes a custom class MinyDict (inspired by addict):


In [1]: from minydra import MinyDict

In [2]: args = MinyDict({"foo": "bar", "yes.no.maybe": "idontknow"}).pretty_print(); args
╭──────────────────────────────╮
│ foo          : bar           │
│ yes.no.maybe : idontknow     │
╰──────────────────────────────╯
Out[2]: {'foo': 'bar', 'yes.no.maybe': 'idontknow'}

In [3]: args.resolve().pretty_print(); args
╭──────────────────────────╮
│ foo : bar                │
│ yes                      │
│ │no                      │
│ │ │maybe : idontknow     │
╰──────────────────────────╯
Out[3]: {'foo': 'bar', 'yes': {'no': {'maybe': 'idontknow'}}}

In [4]: args.yes.no.maybe
Out[4]: "idontknow"

In [5]: "foo" in args
Out[5]: True

In [6]: "rick" in args
Out[6]: False

In [7]: args.morty is None
Out[7]: True

In [8]: args.items()
Out[8]: dict_items([('foo', 'bar'), ('yes', {'no': {'maybe': 'idontknow'}})])

It goes further than addict by adding dumping/loading methods to/from json yaml and pickle and also has a strict mode in MinyDict.update() to prevent the creation of new keys (this is useful to prevent typos in the command-line)

Solution 31 - Python

Simplest solution.

Define a class with only pass statement in it. Create object for this class and use dot notation.

class my_dict:
    pass

person = my_dict()
person.id = 1 # create using dot notation
person.phone = 9999
del person.phone # Remove a property using dot notation

name_data = my_dict()
name_data.first_name = 'Arnold'
name_data.last_name = 'Schwarzenegger'

person.name = name_data
person.name.first_name # dot notation access for nested properties - gives Arnold

Solution 32 - Python

One could use dotsi, for full list, dict and recursive support, with some extension methods

pip install dotsi

and

>>> import dotsi
>>> 
>>> d = dotsi.Dict({"foo": {"bar": "baz"}})     # Basic
>>> d.foo.bar
'baz'
>>> d.users = [{"id": 0, "name": "Alice"}]   # List
>>> d.users[0].name
'Alice'
>>> d.users.append({"id": 1, "name": "Becca"}); # Append
>>> d.users[1].name
'Becca'
>>> d.users += [{"id": 2, "name": "Cathy"}];    # `+=`
>>> d.users[2].name
'Cathy'
>>> d.update({"tasks": [{"id": "a", "text": "Task A"}]});
>>> d.tasks[0].text
'Task A'
>>> d.tasks[0].tags = ["red", "white", "blue"];
>>> d.tasks[0].tags[2];
'blue'
>>> d.tasks[0].pop("tags")                      # `.pop()`
['red', 'white', 'blue']
>>> 
>>> import pprint
>>> pprint.pprint(d)
{'foo': {'bar': 'baz'},
 'tasks': [{'id': 'a', 'text': 'Task A'}],
 'users': [{'id': 0, 'name': 'Alice'},
           {'id': 1, 'name': 'Becca'},
           {'id': 2, 'name': 'Cathy'}]}
>>> 
>>> type(d.users)       # dotsi.Dict (AKA dotsi.DotsiDict)
<class 'dotsi.DotsiList'>
>>> type(d.users[0])    # dotsi.List (AKA dotsi.DotsiList)
<class 'dotsi.DotsiDict'> 
>>> 

Solution 33 - Python

A solution kind of delicate

class DotDict(dict):

    __setattr__ = dict.__setitem__
    __delattr__ = dict.__delitem__

    def __getattr__(self, key):

        def typer(candidate):
            if isinstance(candidate, dict):
                return DotDict(candidate)

            if isinstance(candidate, str):  # iterable but no need to iter
                return candidate

            try:  # other iterable are processed as list
                return [typer(item) for item in candidate]
            except TypeError:
                return candidate

            return candidate

        return typer(dict.get(self, key))

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionbodacydoView Question on Stackoverflow
Solution 1 - Pythonderek73View Answer on Stackoverflow
Solution 2 - PythonepoolView Answer on Stackoverflow
Solution 3 - PythonChris RedfordView Answer on Stackoverflow
Solution 4 - PythonKugelView Answer on Stackoverflow
Solution 5 - PythonDmitry ZotikovView Answer on Stackoverflow
Solution 6 - PythonDaveView Answer on Stackoverflow
Solution 7 - PythontdihpView Answer on Stackoverflow
Solution 8 - PythonPradip GuptaView Answer on Stackoverflow
Solution 9 - PythonvolodymyrView Answer on Stackoverflow
Solution 10 - PythonMike GrahamView Answer on Stackoverflow
Solution 11 - PythonMichael Allan JacksonView Answer on Stackoverflow
Solution 12 - Pythontouch my bodyView Answer on Stackoverflow
Solution 13 - PythonIRSHADView Answer on Stackoverflow
Solution 14 - PythonSenthilView Answer on Stackoverflow
Solution 15 - PythonhardikaView Answer on Stackoverflow
Solution 16 - PythonpbankaView Answer on Stackoverflow
Solution 17 - PythonJayD3eView Answer on Stackoverflow
Solution 18 - PythondeepakView Answer on Stackoverflow
Solution 19 - PythonnehemView Answer on Stackoverflow
Solution 20 - PythonSreeragh A RView Answer on Stackoverflow
Solution 21 - PythonAndrea Di IuraView Answer on Stackoverflow
Solution 22 - PythonGulzarView Answer on Stackoverflow
Solution 23 - PythonEmil StenströmView Answer on Stackoverflow
Solution 24 - PythonmarnixView Answer on Stackoverflow
Solution 25 - PythonarodView Answer on Stackoverflow
Solution 26 - PythonHedde van der HeideView Answer on Stackoverflow
Solution 27 - PythonYaniv K.View Answer on Stackoverflow
Solution 28 - PythonKaroliusView Answer on Stackoverflow
Solution 29 - PythonCaffeinatedMikeView Answer on Stackoverflow
Solution 30 - PythontedView Answer on Stackoverflow
Solution 31 - PythonSreeragh A RView Answer on Stackoverflow
Solution 32 - PythonGulzarView Answer on Stackoverflow
Solution 33 - PythonYonks SomarlView Answer on Stackoverflow