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

226,867

Solution 1

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 2

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 3

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

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

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'])
Share:
226,867
bodacydo
Author by

bodacydo

My name is Boda Cydo. I am from Africa but now live in Washington DC.

Updated on July 08, 2022

Comments

  • bodacydo
    bodacydo almost 2 years

    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': ... } }
    
  • bodacydo
    bodacydo about 14 years
    Thanks for the answer. But take a look at this question that I also just asked: stackoverflow.com/questions/2352252/… This seems like a good idea to use . instead of [] to access complicated data structures in Mako templates.
  • user1066101
    user1066101 about 14 years
    @bodacydo: Mako template parsing is intentionally a simplification of Python syntax with many, many features discarded. Don't confuse Mako syntax with Python syntax. They're entirely different with entirely different purposes.
  • Russell Smith
    Russell Smith about 14 years
    I can see a use case for this; in fact, I did it just a couple weeks ago. In my case I wanted an object that I could access attributes with dot notation. I found it very easy to simply inherit from dict so I get all the dict features built-in, but the public interface to this object uses dot notation (it's essentially a read-only interface to some static data). My users are much happier with 'foo.bar' than with 'foo["bar"]' and I'm happy that I can piggy-back off of the features of the dict datatype.
  • bodacydo
    bodacydo about 14 years
    What does monkeypatch mean exactly? I have heard about it but not used. (Sorry that I ask such newbie questions, I am not that good with programming yet (I'm only 2nd year student.))
  • Mike Graham
    Mike Graham about 14 years
    Monkeypatching is using the dynamicity of Python (or whatever language) to change something that would usually be defined in source code. It especially applies to changing the definition of classes after they are created.
  • bodacydo
    bodacydo about 14 years
    I went with using Bunch from ActiveState code.activestate.com/recipes/… This is good enough for now (until I learn good Python style).
  • user1066101
    user1066101 about 14 years
    @Bryan Oakley: So you built a namedtuple for them? That seems like too much work to me. Why not just create a namedtuple?
  • Russell Smith
    Russell Smith about 14 years
    @S.Lott: I was using an older version of python that doesn't have namedtuple.
  • Larry Hastings
    Larry Hastings about 14 years
    You already know good Python style: we're telling you, don't pretend that the values of a dict are attributes. It's bad practice. For example, what if you want to store a value with the same name as an existing attribute of a dict, like "items" or "get" or "pop"? Probably something confusing. So don't do it!
  • bodacydo
    bodacydo about 14 years
    Oops, I forgot about attributes like 'items', 'get' or 'pop. Thanks for bringing up this important example!
  • bgusach
    bgusach over 9 years
    @Gabe, it has been a long time... but I think it is worth saying. It is not "good enough in JS": it is "horrible enough in JS". It gets funny when you store keys/attr that have the same name as other important attributes in the prototypic chain.
  • JayD3e
    JayD3e almost 9 years
    If you're using this functionality a lot, beware of the speed of Bunch. I was using it pretty frequently and it ended up consuming a third of my request time. Check out my answer for a more detailed explanation of this.
  • dlite922
    dlite922 over 8 years
    :-) can you make it work with keys that have already dot in the name? {"test.foo": "bar"} can be accessed via mymap.test.foo That would be fantastic. It will take some regressesion to convert a flat map to a deep map then apply DotMap to it, but it's worth it!
  • Dmitri
    Dmitri over 8 years
    Neat. Any way to make tab listing / completion work with the keys in Jupyter notebook? Dot-style access is most valuable for interactive use.
  • Chris Redford
    Chris Redford over 8 years
    @Dmitri Cool product. Never heard of it before, so I'm not sure how to make its autocomplete work. I agree using DotMap with autocomplete works best. I use Sublime Text, which autocompletes previously typed keywords.
  • andreas-h
    andreas-h about 8 years
    Very simple answer, great! Do you happen to know what I would need to to in order to have tab-completion in IPython work? The class would need to implement __dir__(self), but somehow I cannot get it to work.
  • tmthyjames
    tmthyjames almost 8 years
    +1 for simplicity. but doesn't seem to work on nested dicts. d = {'foo': {'bar': 'baz'}}; d = dotdict(d); d.foo.bar throws an attribute error, but d.foo work fine.
  • David
    David almost 8 years
    Yep this does not work for complex nested structures.
  • berto
    berto almost 8 years
    To work on Python 3 I updated .iteritems() to .items()
  • mic_e
    mic_e almost 8 years
    Note that this will behave differently from common expectations in that it won't raise AttributeError if the attribute does not exist. Instead it will return None.
  • Shagru
    Shagru over 7 years
    Thanks for the comment about pickling. I was driven crazy by this error and only realized that it was because of this issue!
  • Beatriz Fonseca
    Beatriz Fonseca over 7 years
    I found this code, it has error raise: goodcode.io/articles/python-dict-object
  • Dave
    Dave about 7 years
    This is really neat. You could probably use a defaultdict, like I did below to get nested access.
  • derek73
    derek73 about 7 years
    it does not auto-dotdict its members, so you'd have to do d = {'foo': dotdict({'bar': 'baz'})}; d = dotdict(d); and d.foo.bar works
  • TMKasun
    TMKasun almost 7 years
    @tmthyjames you could simply return dotdict type object in the getter method to recursively access attributes with dot notation like: python class DotDict(dict): """dot.notation access to dictionary attributes""" def __getattr__(*args): val = dict.get(*args) return DotDict(val) if type(val) is dict else val __setattr__ = dict.__setitem__ __delattr__ = dict.__delitem__
  • NichtJens
    NichtJens over 6 years
    Any reason why you use dict.get and not dict.__getitem__?
  • NichtJens
    NichtJens over 6 years
    After experimenting with it, it seems get is indeed a bad idea since it will return None instead of raising an error for missing items ...
  • Admin
    Admin over 6 years
    Recommend adding getstate and setstate so that deep copy and other systems can support it.
  • Admin
    Admin over 6 years
    Also happens when you use copy.deepcopy. This addition is needed.
  • Jens Munk
    Jens Munk over 6 years
    You can simplify your constructor to self.update(*args,**kwargs). Also, you can add __missing__(self,key): value=self[key]= type(self)(); return value. Then you can add missing entries using dot notation. If you want it to be pickable, you can add __getstate__ and __setstate__
  • Simon Streicher
    Simon Streicher over 6 years
    I find that it lacks dictionary extraction for things like **kwargs or c = {**a, **b}. In fact, it fails quietly, it behaves like an empty dictionary when extracting.
  • Chris Redford
    Chris Redford over 6 years
    @SimonStreicher I tested this with m = DotMap(); m.a = 2; m.b = 3; print('{a} {b}'.format(**m)); and I got the expected 2 3. If you have a proven broken case that works for dict() but not DotMap(), please submit your code to the Issues tab in GitHub.
  • Simon Streicher
    Simon Streicher about 6 years
    @ChrisRedford hmm, my Linux Mint 17, with Python version Python 3.6.2 :: Anaconda custom (64-bit) and dotmap version sys.version_info(major=3, minor=6, micro=2, releaselevel='final', serial=0) returns an unexpected KeyError: 'a' for your example. Furthermore (lambda **kwargs:(print(kwargs)))(**DotMap(a=5,b=2)) prints an empty dict {}. The rest seems to work fine, such as DotMap(a=5)['a'] returning 5 as expected. I assumed this was general, not just my system.
  • Simon Streicher
    Simon Streicher about 6 years
    @ChrisRedford I submitted my findings as requested: github.com/drgrib/dotmap/issues/26 , I hope this helps.
  • ch271828n
    ch271828n about 6 years
    what is the use of __setitem__? It seems that without these lines the class works still well
  • epool
    epool about 6 years
    @Turtle you can read more about it here diveintopython.net/object_oriented_framework/…
  • ajsp
    ajsp about 6 years
    Great idea: as far as "style" goes, I would rather have dot notion on dicts rather than brackets with big ugly strings nested in them. Will be using, thanks.
  • Daniel Moskovich
    Daniel Moskovich almost 6 years
    I found it useful to embed this inside a function by placing def docdict(name): before it and then ` if isinstance(name, dict): return DotDict(name) return name `
  • Casimir
    Casimir over 5 years
    Is there a way to retain the dot access when passing the DotDict to a function func(**mydict) and accepting it like def func(**mydict): print(mydict.val)? Currently, this throws an AttributeError: 'dict' object has no attribute 'val'.
  • Xiao
    Xiao over 5 years
    This would make hasattr(Map, 'anystring') is true. which means the hasattr would always return True due to overriding __getattr__`
  • vijay
    vijay over 5 years
    Hey @epool , @ Kugel , Is there any conda package available for this code.It would be very much helpful
  • epool
    epool over 5 years
    @vijay there is not a package for that yet, but feel free to create one if you wish and you could include the link here as a comment.
  • throws_exceptions_at_you
    throws_exceptions_at_you over 5 years
    def attr(**kwargs): o = lambda: None o.__dict__.update(**kwargs) return o
  • D Sievers
    D Sievers about 5 years
    great simple example.. I extended this a little bit so that a nested dict is easily chained, similar to @DanielMoskovich, but also returns leaf nodes correctly for int, string, etc... or null if not found class dotdict(dict): def __getattr__(self, name): if name not in self: return None elif type(self[name]) is dict: return JsonDot(self[name]) else: return self[name]
  • glennstar
    glennstar about 5 years
    It appears that setting m.new_key = { 'foo': 'bar' } means that m.new_key.foo doesn't return bar. Maybe something like; def __setitem__( self, key, value ): super( GenericObject, self ).__setitem__( key, value ) if isinstance( value, dict ): self.__dict__.update( {key: GenericObject( value ) } ) else: self.__dict__.update( {key: value} ) is more appropriate?
  • zeal
    zeal almost 5 years
    @TMKasun thanks for your code that works. How can you write for the nested setter??
  • martineau
    martineau over 4 years
    Simplification: __getattr__ = dict.get
  • sasawatc
    sasawatc over 4 years
    Python 3: replace iteritems() with items() and xrange() with range()
  • ged
    ged over 4 years
    This approach works better. (with json loaded from file)
  • Mojimi
    Mojimi over 4 years
    Does this account for nested dicts?
  • Carson
    Carson about 4 years
  • miku
    miku about 4 years
    @tmthyjames, another way to allow for nesting: gist.github.com/miku/dc6d06ed894bc23dfd5a364b7def5ed8
  • miku
    miku about 4 years
    Nice to include a defaultdict - however this seems to only work when starting a dict from scratch. If we need to convert an existing dict to a "dotdict" recursively. Here's an alternative dotdict which allow to convert existing dict object recursively: gist.github.com/miku/…
  • 9a3eedi
    9a3eedi over 3 years
    This is in my opinion the best answer here. No need to define your own class or anything.
  • Liquidgenius
    Liquidgenius over 3 years
    @ChrisRedford wouldn't you just run toDict() prior to explosion with **kwargs to explode the native dict?
  • Carlos Pinzón
    Carlos Pinzón over 3 years
    For those wanting tab-completion of the items instead of the methods, just add __dir__ = dict.keys
  • MatrixTheatrics
    MatrixTheatrics over 3 years
    No, I'm not sorry for necrobumping. Using attribute access for key retrieval could just work cleanly if not for the ingrained ABC methods, like items or keys. If you have a dot access dict class and all dot accesses are key accesses then that's a clean interface which is more usable and extendable than the bracket access. All non-underscore-prefixed ABC methods should have a global alternative, so that getattr would never meet non-underscore-prefixed names that are not intended as keys. For this to work, new globals should be introduced (a lot) and we alsoneed anoptional prefix for globals
  • MatrixTheatrics
    MatrixTheatrics over 3 years
    For example for v in .values(mydict) instead of for v in mydict.values(). This way you could use a name id alongside the builtin id by referring to the builtin as .id (even though that's not the point of this). I used to have a 'How dare he???' feeling when a name of mine clashed with a builtin. Now, after many years, I just usually don't care (unless it's a type).
  • MatrixTheatrics
    MatrixTheatrics over 3 years
    I'd be very afraid to use this class as it REALLY does not adheres to the Liskov substitution principle. But that's not the main issue, many problems can arise, mostly revolving around accidental attribute creation, some of which could be solved by a more complex implementation while some couldn't. The updated implementation in your repo is quite different, but not in terms of laying out a minefield for the naive.
  • Eli Burke
    Eli Burke about 3 years
    This is a great update to epool's solution and deepak's update. No external dependencies and works well. Hopefully it bubbles up to the top to save folks some sleuthing.
  • SurpriseDog
    SurpriseDog about 3 years
    Is there anyway to get this to represent itself as a dict for functions that test for type()? - I tried adding self.__class__ = dict but it doesn't seem to help.
  • Karolius
    Karolius almost 3 years
    or you can set __getattr__ = dict.__getitem__
  • Mika72
    Mika72 over 2 years
    This is excellent solution, but you can achieve similar result by using dataclasses library.
  • logicOnAbstractions
    logicOnAbstractions over 2 years
    I disagree with the rather dogmatic statement being made here. While it is generally true, the . notation is much clearer and easier to read. I can see plenty of use cases where the chances of it causing an issue are very small.
  • logicOnAbstractions
    logicOnAbstractions over 2 years
    use .items() for python3
  • Gulzar
    Gulzar over 2 years
    Doesn't work if the dict contains lists or even lists of dicts.
  • Gulzar
    Gulzar over 2 years
    For List support and infinite nesting, See this answer
  • S. Stromberg
    S. Stromberg over 2 years
    Nice, I have a function that takes the output from Argparse.parse_args() which returns a namespace instead of a dictionary. I was looking for a nice way to test that function. This is perfect.
  • Ranel Padon
    Ranel Padon about 2 years
    This is similar to my original plan. Simple, neat, and with no dependencies. Works also on nested cases. I modified the last line though to return None instead of {} for consistency with the dict.get() for non-existing keys: return current_data or None. Cheers!
  • Gulzar
    Gulzar about 2 years
    Another version of this which works for more types
  • Trevor Gross
    Trevor Gross about 2 years
    Very elegant - one helpful addition though! You should reimplement __getattr__ to raise an AttributeError rather than KeyError if not found, so getattr(d, 'nonexistant', default) works properly. It's easy: def __getattr__(self, key:str)->Any: try: return self.__getitem__(key) except KeyError as ex: raise AttributeError(key) from ex`
  • K.A
    K.A about 2 years
    just note : To work on Python 3 I updated .iteritems() to .items()
  • rv.kvetch
    rv.kvetch almost 2 years
    @TrevorGross after some thought, I would personally say I disagree. The first reason is that it's a bit slower to implement it this way, since it involves an extra function call. The other reason though is that I think in is actually shorter and more pythonic to use in general - i.e., 'key' in d instead of getattr(d, 'key').
  • rv.kvetch
    rv.kvetch almost 2 years
    the performance is actually good, so I was surprised. however, the code is terribly written and probably could do with a code linting tool or similar. this lib does outperform most others on here, like box otherwise. In summary, just the code quality needs work imo.
  • Trevor Gross
    Trevor Gross almost 2 years
    Hey @rv.kvetch - __getattr__ isn't just for getattr(), it's also what defines access for dot notation. So, in the example, mydict.not_exist will raise a KeyError because that's what dict.get() does. Generally though, any_cls_instance.not_exist raises AttributeError, and you'd expect the same from this dict class for compatibility. Making getattr(d, 'key', default) work is just a nice effect (d.get('key', default) is the equivalent). A single function call is negligible. This all won't matter in many implementations, but it does where object compatibility is required.
  • rv.kvetch
    rv.kvetch almost 2 years
    right, that's certainly good point, but I still think d.get('key', default) is the way to go here; also it's a little bit shorter to write out I guess. Though I totally get wanting the hasattr and getattr to work, but I feel either a in check or get would probably be a little better.
  • Trevor Gross
    Trevor Gross almost 2 years
    If you need it to work like a dictionary then yes. If you need it to work like a class then no. I think you're still missing the point - it's not only about personal use of getattr, it's about being able to pass it to anything that take a class as an input, not a dict, and potentially relies on EAFP (very common in python). getattr is just one such function that replies on EAFP - any python library, or your own code from before, might also use it.