Package: dotsi
DotsiDot-accessible, update-aware Python dicts (& lists). Works recursively, like a charm.Dotsi defines two classes,dotsi.Dictanddotsi.List, whichwork togetherto bring JavaScript-like dot-notation to Python dicts (and lists therein).Installationpip install dotsiAlternately, downloaddotsi.pyit into your project directory.UsageLet's dive right in:>>>importdotsi>>>>>>d=dotsi.Dict({"foo":{"bar":"baz"}})# Basic>>>d.foo.bar'baz'>>>d.users=[{"id":0,"name":"Alice"}]# List>>>d.users[0].name'Alice'>>>d.users.append({"id":1,"name":"Becca"});# Append>>>d.users[1].name'Becca'>>>d.users+=[{"id":2,"name":"Cathy"}];# `+=`>>>d.users[2].name'Cathy'>>>d.update({"tasks":[{"id":"a","text":"Task A"}]});>>>d.tasks[0].text'Task A'>>>d.tasks[0].tags=["red","white","blue"];>>>d.tasks[0].tags[2];'blue'>>>d.tasks[0].pop("tags")# `.pop()`['red','white','blue']>>>>>>importpprint>>>pprint.pprint(d){'foo':{'bar':'baz'},'tasks':[{'id':'a','text':'Task A'}],'users':[{'id':0,'name':'Alice'},{'id':1,'name':'Becca'},{'id':2,'name':'Cathy'}]}>>>>>>type(d.users)# dotsi.Dict (AKA dotsi.DotsiDict)<class'dotsi.DotsiList'>>>>type(d.users[0])# dotsi.List (AKA dotsi.DotsiList)<class'dotsi.DotsiDict'>>>>In the above example, while we explicitly initializeddas andotsi.Dict:d.usersautomatically became adotsi.List.d.users[0]automatically became adotsi.Dict.Dotsi vs OthersAddict:At Polydojo, we've been usingAddictfor quite some time. It's a great library! But it doesn't play well with list-nested (inner) dicts.>>>importaddict>>>>>>d=addict.Dict({"foo":{"bar":"baz"}})>>>d.foo{'bar':'baz'}>>>d.users=[{"id":0,"name":"Alice"}]>>>d.users[0].nameTraceback(mostrecentcalllast):File"<stdin>",line1,in<module>AttributeError:'dict'objecthasnoattribute'name'>>>EasyDict:EasyDictis another great library. It works recursively, but doesn't fully support list-nested dict updates.>>>importeasydict>>>>>>d=easydict.EasyDict({"foo":{"bar":"baz"}})>>>d.foo{'bar':'baz'}>>>d.users=[{"id":0,"name":"Alice"}]>>>d.users[0].name'Alice'>>>d.users.append({"id":1,"name":"Becca"});>>>d.users[1].nameTraceback(mostrecentcalllast):File"<stdin>",line1,in<module>AttributeError:'dict'objecthasnoattribute'name'>>>ShortcutsClasses:dotsi.Dictis a short alias fordotsi.DotsiDict.dotsi.Listis a short alias fordotsi.DotsiList.Functions:dotsi.dotsify()callsdotsi.Dict/dotsi.List, as appropriate.dotsi.fy()is a short alias fordotsi.dotsify().dotsi.mapdotsify()is like the built-inmap(), but returns adotsi.List.dotsi.mapfyis a short alias fordotsi.mapdotsify(). (More on this below.)In most cases, all you need is:dotsi.fy(thing), wherethingis adictorlist.Dict-Like ObjectsWhiledotsi.fy()converts objects of typedicttodotsi.Dict, itdoesn'ttouch other dict-like objects, such as those of typecollections.OrderedDictorhttp.cookies.SimpleCookie.To convert a non-dict, but dict-like object todotsi.Dict, usedotsi.Dict(.)directly, or usedotsi.fy(dict(.)).>>>importdotsi>>>fromcollectionsimportOrderedDict>>>>>>d=OrderedDict({"foo":{"bar":"baz"}})>>>dOrderedDict([('foo',{'bar':'baz'})])>>>type(d)<class'collections.OrderedDict'>>>>>>>x=dotsi.fy(d)>>>xOrderedDict([('foo',{'bar':'baz'})])>>>type(x)<class'collections.OrderedDict'>>>>>>>y=dotsi.Dict(d)>>>y{'foo':{'bar':'baz'}}>>>type(y)<class'dotsi.DotsiDict'>>>>>>>z=dotsi.fy(dict(d))>>>z{'foo':{'bar':'baz'}}>>>type(z)<class'dotsi.DotsiDict'>Subclasses ofdict, such ashttp.cookie.SimpleCookie, often implement custom behavior, which would be lost on conversion todotsi.Dict. Thus, automatic conversion shouldn't be implemented.Quick PlugDotsi is built and maintained by the folks atPolydojo, Inc., led by Sumukh Barve. If your team is looking for a simple project management tool, please check out our latest product:BoardBell.com.List-Like ObjectsLike with dicts,dotsi.fy(.)only converts objects of typelisttodotsi.List, but doesn't touch other list-like objects or tuples. To convert a non-list, but list-like object todotsi.List, directly calldotsi.List(.)or usedotsi.fy(list(.))Identity FunctionFor non-dictand non-listobjects,dotsi.fy(.)is equivalent to the identity function.Kindly note that from Python3+, the built-inmap()produces a non-listiterable. Thus, callingdotsi.fy(map(.))is equivalent to justmap(.). Instead, please usedotsi.List(map(.)).Mapping HelperAs mapping is a pretty-common use case, we've includeddotsi.mapfy(.), which is essentially equivalent todotsi.List(map(.)). But additionally, withdotsi.mapfy(.), for mapping onto asinglesequence, you may pass arguments in either order.That is, the following lines are equivalent:x = dotsi.mapfy(lambda n: {"n": n}, [0, 1, 2])x = dotsi.mapfy([0, 1, 2], lambda n: {"n": n})In either case,x[0].n == 0will beTrue.When mapping ontomultiplesequences,dotsi.mapfy(.)expects the same order of arguments asmap(.).Overridden MethodsExcluding magic-methods like.__init__(.)etc., methods overridden by Dotsi are listed below.dotsi.Dictoverrides:.update(.).setdefault(.).copy(.)dotsi.Listoverrides:insert(.)append(.)extend(.)copy(.)Signatures for all overridden methods should be equivalent (if not exactly identical) to their non-overridden counterparts.LicensingCopyright (c) 2020 Polydojo, Inc.Software Licensing:The software is released "AS IS" under theMIT license, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED. Kindly seeLICENSE.txtfor more details.No Trademark Rights:The above software licensing termsdo notgrant any right in the trademarks, service marks, brand names or logos of Polydojo, Inc.