Package: dictdot
dictdotPython dict accessible by dot, similar to how it's done withan object in Javascript.It provides the same nature of adict, plus facilities in notation and afindfunctionality that helps in the data exploration process. Particularly useful when dealing with a large JSON that you don't know much about.It's implemented by overriding some builtin methods of regular Pythondict, then it has no impact in performance or memory footprint. Nevertheless, for productive code it's always recommended to use the native Python dictionaries.UsageMinimal examplefromdictdotimportdictdotd=dictdot()# Get and set items either by dot or regular notation.d["foo"]=1d.bar=1assertd.foo==d["bar"]# Nested dicts are also converted to dictdot.d.bar=[{"baz":1}]asserttype(d.bar[0])isdictdot# Find elements by key or value.assert[".foo",".bar[0].baz"]==list(dictdot.find(d,check_value=1))Performance analysisNow let's see a more illustrative use case. We'll be usingthis ~25MB JSON file. If you havecurlinstalled in your terminal, you can download it as follows:curl-JOhttps://raw.githubusercontent.com/json-iterator/test-data/master/large-file.jsonThe following Python code can be run as it is if you havedictdotinstalled andlarge-file.jsonin the current directory. It will load the file in memory, then convert it todictdot, and perform somefindtasks on it, measuring the time in each step. I share the time for each step running it in my 2018 laptop (2.6GHz CPU)):importjsonimporttimefromdictdotimportdictdotdefget_time(old_t=None):new_t=time.time()ifold_t:print(f"Time:{new_t-old_t:.2f}sec\n")returnnew_tt=get_time()print("Load data from file.")withopen("large-file.json")asf:data=json.load(f)t=get_time(t)# Time: 0.36 secprint("Convert to `dictdot`.")data=[dictdot(d)fordindata]t=get_time(t)# Time: 4.67 secprint("List all keys.")ks=list(dictdot.find(data))print(f"{len(ks)}keys found.")t=get_time(t)# 626243 keys found.# Time: 5.53 secprint("Find values by function.")vs=list(dictdot.find(data,check_value=lambdav:type(v)isstrand" dict "inv))print(f"{len(vs)}values found.")t=get_time(t)# 2 values found.# Time: 0.62 secprint("Convert back to dict.")dic=[d.as_dict()fordindata]t=get_time(t)# Time: 0.38 secFinding valuesIn the example above, the variablevscontains paths that represent items withindata. These items match the condition of being strings, and containing the substring" dict "(with trailing spaces). Let's check them:vs[0]# '[169].payload.comment.body'data[169].payload.comment.body# "What about making the combined dict a local variable, like...Comparing the notationWhich prefer?vs[1]# '[1275].payload.commits[0].message'assertdata[1275].payload.commits[0].messageis\data[1275]["payload"]["commits"][0]["message"]Feature summaryCan initializedictdotthe same way asdict:a=["foo","bar","baz"]b=range(3)d=dictdot(zip(a,b))Access items by dot notation, or as adict:assertd.foo<d["bar"]<d.get("baz")Also when setting an item:d.bar={"fee":1,"boo":None,}Convert to regulardict:d2=d.as_dict()assertd==d2asserttype(d2)isdictFunction to find keys and values nested in the dict structure:# Find every key equal to "foo".assertlist(dictdot.find(d,check_key="foo"))==[".foo"]# Find every value that bool-evaluates to False.assertlist(dictdot.find(d,check_value=lambdav:notv))==[".foo",".bar.boo"]# Both key and value must evaluate to True.assertlist(dictdot.find(d,check_key="bar",check_value=1))==[]See thetests in the source codefor more details about the behavior and usage.