I dislike adding another log to a (more than) 10-year old fire, but I'd also check out the dotwiz library, which I've recently released - just this year actually.
It's a relatively tiny library, which also performs really well for get (access) and set (create) times in benchmarks, at least as compared to other alternatives.
Install dotwiz via pip
pip install dotwizIt does everything you want it to do and subclasses dict, so it operates like a normal dictionary:
from dotwiz import DotWizdw = DotWiz()dw.hello = 'world'dw.hellodw.hello += '!'# dw.hello and dw['hello'] now both return 'world!'dw.val = 5dw.val2 = 'Sam'On top of that, you can convert it to and from dict objects:
d = dw.to_dict()dw = DotWiz(d) # automatic conversion in constructorThis means that if something you want to access is already in dict form, you can turn it into a DotWiz for easy access:
import jsonjson_dict = json.loads(text)data = DotWiz(json_dict)print data.location.cityFinally, something exciting I am working on is an existing feature request so that it automatically creates new child DotWiz instances so you can do things like this:
dw = DotWiz()dw['people.steve.age'] = 31dw# ✫(people=✫(steve=✫(age=31)))Comparison with dotmap
I've added a quick and dirty performance comparison with dotmap below.
First, install both libraries with pip:
pip install dotwiz dotmapI came up with the following code for benchmark purposes:
from timeit import timeitfrom dotwiz import DotWizfrom dotmap import DotMapd = {'hey': {'so': [{'this': {'is': {'pretty': {'cool': True}}}}]}}dw = DotWiz(d)# ✫(hey=✫(so=[✫(this=✫(is=✫(pretty={'cool'})))]))dm = DotMap(d)# DotMap(hey=DotMap(so=[DotMap(this=DotMap(is=DotMap(pretty={'cool'})))]))assert dw.hey.so[0].this['is'].pretty.cool == dm.hey.so[0].this['is'].pretty.cooln = 100_000print('dotwiz (create): ', round(timeit('DotWiz(d)', number=n, globals=globals()), 3))print('dotmap (create): ', round(timeit('DotMap(d)', number=n, globals=globals()), 3))print('dotwiz (get): ', round(timeit("dw.hey.so[0].this['is'].pretty.cool", number=n, globals=globals()), 3))print('dotmap (get): ', round(timeit("dm.hey.so[0].this['is'].pretty.cool", number=n, globals=globals()), 3))Results, on my M1 Mac, running Python 3.10:
dotwiz (create): 0.189dotmap (create): 1.085dotwiz (get): 0.014dotmap (get): 0.335