How to use a dot "." to access members of dictionary?
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 childdict
instances toDotMap
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'])
bodacydo
My name is Boda Cydo. I am from Africa but now live in Washington DC.
Updated on July 08, 2022Comments
-
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 writemydict.val
.Also I'd like to access nested dicts this way. For example
mydict.mydict2.val
would refer to
mydict = { 'mydict2': { 'val': ... } }
-
bodacydo about 14 yearsThanks 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 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 about 14 yearsI 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 about 14 yearsWhat 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 about 14 yearsMonkeypatching 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 about 14 yearsI went with using Bunch from ActiveState code.activestate.com/recipes/… This is good enough for now (until I learn good Python style).
-
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 about 14 years@S.Lott: I was using an older version of python that doesn't have namedtuple.
-
Larry Hastings about 14 yearsYou 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 about 14 yearsOops, I forgot about attributes like 'items', 'get' or 'pop. Thanks for bringing up this important example!
-
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 almost 9 yearsIf 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 over 8 years:-) can you make it work with keys that have already dot in the name?
{"test.foo": "bar"}
can be accessed viamymap.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 over 8 yearsNeat. 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 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 about 8 yearsVery 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 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, butd.foo
work fine. -
David almost 8 yearsYep this does not work for complex nested structures.
-
berto almost 8 yearsTo work on Python 3 I updated
.iteritems()
to.items()
-
mic_e almost 8 yearsNote that this will behave differently from common expectations in that it won't raise
AttributeError
if the attribute does not exist. Instead it will returnNone
. -
Shagru over 7 yearsThanks for the comment about pickling. I was driven crazy by this error and only realized that it was because of this issue!
-
Beatriz Fonseca over 7 yearsI found this code, it has error raise: goodcode.io/articles/python-dict-object
-
Dave about 7 yearsThis is really neat. You could probably use a defaultdict, like I did below to get nested access.
-
derek73 about 7 yearsit does not auto-dotdict its members, so you'd have to do
d = {'foo': dotdict({'bar': 'baz'})}; d = dotdict(d);
andd.foo.bar
works -
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 over 6 yearsAny reason why you use
dict.get
and notdict.__getitem__
? -
NichtJens over 6 yearsAfter experimenting with it, it seems
get
is indeed a bad idea since it will returnNone
instead of raising an error for missing items ... -
Admin over 6 yearsRecommend adding getstate and setstate so that deep copy and other systems can support it.
-
Admin over 6 yearsAlso happens when you use copy.deepcopy. This addition is needed.
-
Jens Munk over 6 yearsYou 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 over 6 yearsI find that it lacks dictionary extraction for things like
**kwargs
orc = {**a, **b}
. In fact, it fails quietly, it behaves like an empty dictionary when extracting. -
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 expected2 3
. If you have a proven broken case that works fordict()
but notDotMap()
, please submit your code to the Issues tab in GitHub. -
Simon Streicher about 6 years@ChrisRedford hmm, my Linux Mint 17, with Python version
Python 3.6.2 :: Anaconda custom (64-bit)
and dotmap versionsys.version_info(major=3, minor=6, micro=2, releaselevel='final', serial=0)
returns an unexpectedKeyError: '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 asDotMap(a=5)['a']
returning5
as expected. I assumed this was general, not just my system. -
Simon Streicher about 6 years@ChrisRedford I submitted my findings as requested: github.com/drgrib/dotmap/issues/26 , I hope this helps.
-
ch271828n about 6 yearswhat is the use of
__setitem__
? It seems that without these lines the class works still well -
epool about 6 years@Turtle you can read more about it here diveintopython.net/object_oriented_framework/…
-
ajsp about 6 yearsGreat 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 almost 6 yearsI 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 over 5 yearsIs there a way to retain the dot access when passing the
DotDict
to a functionfunc(**mydict)
and accepting it likedef func(**mydict): print(mydict.val)
? Currently, this throws anAttributeError: 'dict' object has no attribute 'val'
. -
Xiao over 5 yearsThis would make
hasattr(Map, 'anystring') is true. which means the hasattr would always return True due to overriding
__getattr__` -
vijay over 5 yearsHey @epool , @ Kugel , Is there any conda package available for this code.It would be very much helpful
-
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 over 5 yearsdef attr(**kwargs): o = lambda: None o.__dict__.update(**kwargs) return o
-
D Sievers about 5 yearsgreat 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 about 5 yearsIt appears that setting
m.new_key = { 'foo': 'bar' }
means thatm.new_key.foo
doesn't returnbar
. 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 almost 5 years@TMKasun thanks for your code that works. How can you write for the nested setter??
-
martineau over 4 yearsSimplification:
__getattr__ = dict.get
-
sasawatc over 4 yearsPython 3: replace
iteritems()
withitems()
andxrange()
withrange()
-
ged over 4 yearsThis approach works better. (with json loaded from file)
-
Mojimi over 4 yearsDoes this account for nested dicts?
-
Carson about 4 yearsNot support nested Dict. docs.python.org/3.3/library/types.html#types.SimpleNamespace
-
miku about 4 years@tmthyjames, another way to allow for nesting: gist.github.com/miku/dc6d06ed894bc23dfd5a364b7def5ed8
-
miku about 4 yearsNice 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 existingdict
object recursively: gist.github.com/miku/… -
9a3eedi over 3 yearsThis is in my opinion the best answer here. No need to define your own class or anything.
-
Liquidgenius over 3 years@ChrisRedford wouldn't you just run toDict() prior to explosion with **kwargs to explode the native dict?
-
Carlos Pinzón over 3 yearsFor those wanting tab-completion of the items instead of the methods, just add
__dir__ = dict.keys
-
MatrixTheatrics over 3 yearsNo, 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
orkeys
. 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 over 3 yearsFor example
for v in .values(mydict)
instead offor v in mydict.values()
. This way you could use a nameid
alongside the builtinid
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 over 3 yearsI'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 about 3 years
-
SurpriseDog about 3 yearsIs there anyway to get this to represent itself as a dict for functions that test for
type()
? - I tried addingself.__class__ = dict
but it doesn't seem to help. -
Karolius almost 3 yearsor you can set
__getattr__ = dict.__getitem__
-
Mika72 over 2 yearsThis is excellent solution, but you can achieve similar result by using
dataclasses
library. -
logicOnAbstractions over 2 yearsI 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 over 2 yearsuse .items() for python3
-
Gulzar over 2 yearsDoesn't work if the dict contains lists or even lists of dicts.
-
Gulzar over 2 yearsFor List support and infinite nesting, See this answer
-
S. Stromberg over 2 yearsNice, 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 about 2 yearsThis 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 thedict.get()
for non-existing keys:return current_data or None
. Cheers! -
Gulzar about 2 yearsAnother version of this which works for more types
-
Trevor Gross about 2 yearsVery elegant - one helpful addition though! You should reimplement
__getattr__
to raise anAttributeError
rather thanKeyError if not found
, sogetattr(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 about 2 yearsjust note : To work on Python 3 I updated .iteritems() to .items()
-
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 ofgetattr(d, 'key')
. -
rv.kvetch almost 2 yearsthe 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 almost 2 yearsHey @rv.kvetch -
__getattr__
isn't just forgetattr()
, it's also what defines access for dot notation. So, in the example,mydict.not_exist
will raise aKeyError
because that's whatdict.get()
does. Generally though,any_cls_instance.not_exist
raisesAttributeError
, and you'd expect the same from this dict class for compatibility. Makinggetattr(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 almost 2 yearsright, 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 thehasattr
andgetattr
to work, but I feel either ain
check orget
would probably be a little better. -
Trevor Gross almost 2 yearsIf 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.