7

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
Alejandro
  • 879
  • 11
  • 27
  • 2
    `collections.namedtuple`, as suggested in the thread you link? – dspencer Mar 14 '20 at 15:22
  • 1
    or something like this --> https://github.com/Infinidat/munch – gold_cy Mar 14 '20 at 15:28
  • Does this answer your question? [Accessing dict keys like an attribute?](https://stackoverflow.com/questions/4984647/accessing-dict-keys-like-an-attribute) – Friedrich -- Слава Україні Mar 14 '20 at 16:20
  • Why do you want to do this? – AMC Mar 14 '20 at 16:31
  • there's also an AttrDict object. see https://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. – JL Peyret Mar 14 '20 at 17:08

4 Answers4

6

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.

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)})
Lior Cohen
  • 5,570
  • 2
  • 14
  • 30
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()
dspencer
  • 4,297
  • 4
  • 22
  • 43
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'