8

What I am looking for is having something like this:

self.info['key'].attr2

But I'm not sure what would be the most elegant way to achieve it. So far I have defined a dictionary like below but don't have attributes from it.

self.info  = {}
self.info.update({'key':
                        {'attr1':   2,
                         'attr2':   lambda: self.func(arg1)}
                       })

But then I would have to use it like:

self.info['key']['attr2']()

Note that what I am looking for is accessing the dictionary values like attributes. This thread has an answer which can be used in my case also probably that is I make an object out of the sub-dictionary from above and give it as a value to the main dictionary.

But I wonder if there is nicer way, perhaps with decorators and with less lines of codes and maybe even without using dictionaries do to something like what I described in the first line, perhaps even better like:

self['key'].attr2
5
  • 2
    collections.namedtuple, as suggested in the thread you link? Commented Mar 14, 2020 at 15:22
  • 1
    or something like this --> github.com/Infinidat/munch Commented Mar 14, 2020 at 15:28
  • Does this answer your question? Accessing dict keys like an attribute? Commented Mar 14, 2020 at 16:20
  • Why do you want to do this? Commented Mar 14, 2020 at 16:31
  • there's also an AttrDict object. see github.com/bcj/AttrDict not sure what the author means with the read-only status though, might be time to look for alternative packages. one advantage is that you can still use dictionary methods, i.e, o.attr2 or o.get("attr2"). mostly, the SimpleNamespace answer would be my pick. Commented Mar 14, 2020 at 17:08

4 Answers 4

9

Here is a short wrapper to access dict keys as attributes:

class nameddict(dict):
   __getattr__ = dict.__getitem__

info = nameddict()
info['key'] = nameddict(attr1=2, attr2=3)

Then info['key'].attr2 as well as info['key']['attr2'] and info.key.attr2 returns 3.

Sign up to request clarification or add additional context in comments.

Comments

4

if you want a more dynamic container for which you don't need to declare the attributes like in namedtuple you can use SimpleNamespace object.

from types import SimpleNamespace as ns
info  = {}
info.update({'key': ns(attr1=2, attr2=lambda x: x+1)})

4 Comments

This is only Python 3.
@Lior what if the names 'attr1' and 'attr2' were given to me as strings in the begining.
Is it really a safe way to use SimpleNameSpace en masse?
@Friedrich--СлаваУкраїні this is 2020 (and now 2023) ... of course it's python 3, get with the times!
2

You could use collections.namedtuple, as suggested in the thread you link:

Attrs = namedtuple("Attrs", ["attr1", "attr2"])
info = {"key": Attrs(attr1=2, attr2=lambda: 4)}
info["key"].attr2()

Comments

0

There's a library for that called Box. Very convenient. https://pypi.org/project/python-box/

It handles all kinds of edge cases, like dictionaries inside dictionaries and lists etc.

pip install python-box

from box import Box

movie_box = Box({
    "Robin Hood: Men in Tights": {
        "imdb_stars": 6.7,
        "length": 104,
        "stars": [ {"name": "Cary Elwes", "imdb": "nm0000144", "role": "Robin Hood"},
                   {"name": "Richard Lewis", "imdb": "nm0507659", "role": "Prince John"} ]
    }
})

movie_box.Robin_Hood_Men_in_Tights.imdb_stars
# 6.7

movie_box.Robin_Hood_Men_in_Tights.stars[0].name
# 'Cary Elwes'


Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.