How to use dot notation for dict in python?

PythonDictionaryNested

Python Problem Overview


I'm very new to python and I wish I could do . notation to access values of a dict.

Lets say I have test like this:

>>> test = dict()
>>> test['name'] = 'value'
>>> print(test['name'])
value

But I wish I could do test.name to get value. Infact I did it by overriding the __getattr__ method in my class like this:

class JuspayObject:

	def __init__(self,response):
    	self.__dict__['_response'] = response

    def __getattr__(self,key): 
    	try:
	    	return self._response[key]
	    except KeyError,err:
	    	sys.stderr.write('Sorry no key matches')

and this works! when I do:

test.name // I get value.

But the problem is when I just print test alone I get the error as:

'Sorry no key matches'

Why is this happening?

Python Solutions


Solution 1 - Python

This functionality already exists in the standard libraries, so I recommend you just use their class.

>>> from types import SimpleNamespace
>>> d = {'key1': 'value1', 'key2': 'value2'}
>>> n = SimpleNamespace(**d)
>>> print(n)
namespace(key1='value1', key2='value2')
>>> n.key2
'value2'

Adding, modifying and removing values is achieved with regular attribute access, i.e. you can use statements like n.key = val and del n.key.

To go back to a dict again:

>>> vars(n)
{'key1': 'value1', 'key2': 'value2'}

The keys in your dict should be string identifiers for attribute access to work properly.

Simple namespace was added in Python 3.3. For older versions of the language, argparse.Namespace has similar behaviour.

Solution 2 - Python

I assume that you are comfortable in Javascript and want to borrow that kind of syntax... I can tell you by personal experience that this is not a great idea.

It sure does look less verbose and neat; but in the long run it is just obscure. Dicts are dicts, and trying to make them behave like objects with attributes will probably lead to (bad) surprises.

If you need to manipulate the fields of an object as if they were a dictionary, you can always resort to use the internal __dict__ attribute when you need it, and then it is explicitly clear what you are doing. Or use getattr(obj, 'key') to have into account the inheritance structure and class attributes too.

But by reading your example it seems that you are trying something different... As the dot operator will already look in the __dict__ attribute without any extra code.

Solution 3 - Python

In addition to this answer, one can add support for nested dicts as well:

from types import SimpleNamespace

class NestedNamespace(SimpleNamespace):
    def __init__(self, dictionary, **kwargs):
        super().__init__(**kwargs)
        for key, value in dictionary.items():
            if isinstance(value, dict):
                self.__setattr__(key, NestedNamespace(value))
            else:
                self.__setattr__(key, value)
                
nested_namespace = NestedNamespace({
    'parent': {
        'child': {
            'grandchild': 'value'
        }
    },
    'normal_key': 'normal value',
})


print(nested_namespace.parent.child.grandchild)  # value
print(nested_namespace.normal_key)  # normal value

Note that this does not support dot notation for dicts that are somewhere inside e.g. lists.

Solution 4 - Python

Could you use a named tuple?

from collections import namedtuple
Test = namedtuple('Test', 'name foo bar')
my_test = Test('value', 'foo_val', 'bar_val')
print(my_test)
print(my_test.name)

Solution 5 - Python

__getattr__ is used as a fallback when all other attribute lookup rules have failed. When you try to "print" your object, Python look for a __repr__ method, and since you don't implement it in your class it ends up calling __getattr__ (yes, in Python methods are attributes too). You shouldn't assume which key getattr will be called with, and, most important, __getattr__ must raise an AttributeError if it cannot resolve key.

As a side note: don't use self.__dict__ for ordinary attribute access, just use the plain attribute notation:

class JuspayObject:

    def __init__(self,response):
        # don't use self.__dict__ here
        self._response = response

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

Now if your class has no other responsability (and your Python version is >= 2.6 and you don't need to support older versions), you may just use a namedtuple : http://docs.python.org/2/library/collections.html#collections.namedtuple

Solution 6 - Python

You can use the built-in method argparse.Namespace():

import argparse

args = argparse.Namespace()
args.name = 'value'

print(args.name)
# 'value'

You can also get the original dict via vars(args).

Solution 7 - Python

You have to be careful when using __getattr__, because it's used for a lot of builtin Python functionality.

Try something like this...

class JuspayObject:

    def __init__(self,response):
        self.__dict__['_response'] = response

    def __getattr__(self, key):
        # First, try to return from _response
        try:
            return self.__dict__['_response'][key]
        except KeyError:
            pass
        # If that fails, return default behavior so we don't break Python
        try:
            return self.__dict__[key]
        except KeyError:
            raise AttributeError, key

>>> j = JuspayObject({'foo': 'bar'})
>>> j.foo
'bar'
>>> j
<__main__.JuspayObject instance at 0x7fbdd55965f0>

Solution 8 - Python

Here is a simple, handy dot notation helper example that is working with nested items:

def dict_get(data:dict, path:str, default = None):
    pathList = re.split(r'\.', path, flags=re.IGNORECASE)
    result = data
    for key in pathList:
        try:
            key = int(key) if key.isnumeric() else key 
            result = result[key]
        except:
            result = default
            break
    
    return result

Usage example:

my_dict = {"test1": "str1", "nested_dict": {"test2": "str2"}, "nested_list": ["str3", {"test4": "str4"}]}
print(dict_get(my_dict, "test1"))
# str1
print(dict_get(my_dict, "nested_dict.test2"))
# str2
print(dict_get(my_dict, "nested_list.1.test4"))
# str4

Solution 9 - Python

With a small addition to this answer you can support lists as well:

class NestedNamespace(SimpleNamespace):
def __init__(self, dictionary, **kwargs):
    super().__init__(**kwargs)
    for key, value in dictionary.items():
        if isinstance(value, dict):
            self.__setattr__(key, NestedNamespace(value))
        elif isinstance(value, list):
            self.__setattr__(key, map(NestedNamespace, value))
        else:
            self.__setattr__(key, value)

Solution 10 - Python

I use the dotted_dict package:

>>> from dotted_dict import DottedDict
>>> test = DottedDict()
>>> test.name = 'value'
>>> print(test.name)
value

Solution 11 - Python

Add a __repr__() method to the class so that you can customize the text to be shown on

print text

Learn more here: https://web.archive.org/web/20121022015531/http://diveintopython.net/object_oriented_framework/special_class_methods2.html

Solution 12 - Python

#!/usr/bin/env python3


import json
from sklearn.utils import Bunch
from collections.abc import MutableMapping


def dotted(inpt: MutableMapping,
           *args,
           **kwargs
           ) -> Bunch:
    """
    Enables recursive dot notation for ``dict``.
    """

    return json.loads(json.dumps(inpt),
                      object_hook=lambda x:
                      Bunch(**{**Bunch(), **x}))

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
QuestionbatmanView Question on Stackoverflow
Solution 1 - PythonwimView Answer on Stackoverflow
Solution 2 - PythonfortranView Answer on Stackoverflow
Solution 3 - PythonMichael H.View Answer on Stackoverflow
Solution 4 - PythonYannView Answer on Stackoverflow
Solution 5 - Pythonbruno desthuilliersView Answer on Stackoverflow
Solution 6 - PythonsecsilmView Answer on Stackoverflow
Solution 7 - PythonAyaView Answer on Stackoverflow
Solution 8 - PythonTudor CorcimarView Answer on Stackoverflow
Solution 9 - PythonFoGyuriView Answer on Stackoverflow
Solution 10 - PythonJames HirschornView Answer on Stackoverflow
Solution 11 - Pythonuser2286078View Answer on Stackoverflow
Solution 12 - PythonFrankthetankView Answer on Stackoverflow