Package: trict
trictPython dictionary with extra stuff.Starting off, if you're seeing this, please give feedback!You can install withpip install trictorconda install -c saska trict(working on conda-forge). Doesn't currently have any dependencies (although you'll needpytestif you want to run the tests for some reason), only works with python>3.6 strictly because I like f-strings which I can reformat out if someone needs this to work on something else.Tricktionaries (subclass of collections.UserDict) are dictionary-type things that have recursive (and other) helper things I've previously needed. Here's how they work:You initialize a tricktionary with a normal dictionary.>>>fromtrictimportTrict>>>t=Trict({'user':{'information':{'attribute':'infonugget','another_attribute':'secondnugget'},'moreinformation':'extranugget'}})You can then perform all sorts of cool operations with it, like period-separated setting and getting (or any-string-separated, you can specifykey_sepas a keyword argument in the Trict constructor, default is.).>>>t['user.information.attribute']'infonugget'Just like a python dictionary, Trict implementsget(key, default=None)if you don't like KeyErrors.When setting, the Trict will create intermediary keys for you:>>>t['user.newinformation.newattribute']='new'>>>t['user.newinformation.newattribute']'new'Deleting also works as you'd expect, so you can also.pop(). You can also do any of this using sequences as keys.>>>key=['user','information','another_attribute']>>>t[key]'secondnugget'If you want to flatten your dictionary for a csv export for example (use keys as the header), you can do that. The constructor argkey_sepalso affects how keys are formatted here. The Tricktionary can be rebuilt by passing this type of flattened dictionary into the alternatefrom_flat_dictconstructor with the samekey_sep.>>>t.flatten(){'user.information.attribute':'infonugget','user.information.another_attribute':'secondnugget','user.moreinformation':'extranugget','user.newinformation.newattribute':'new'}Or do a complete traversal withtraverse. It returns a generator yielding 2-tuples of (key-path, value).>>>fork,vint.traverse():...print(f"{k}:{v}")['user']:{'information':{'attribute':'infonugget','another_attribute':'secondnugget'},'moreinformation':'extranugget','newinformation':{'newattribute':'new'}}['user','information']:{'attribute':'infonugget','another_attribute':'secondnugget'}['user','information','attribute']:infonugget['user','information','another_attribute']:secondnugget['user','moreinformation']:extranugget['user','newinformation']:{'newattribute':'new'}['user','newinformation','newattribute']:newIf you just need the leaves, you can do that as well with a similar format.>>>fork,vint.leaves():...print(f"{k}:{v}")['user','information','attribute']:infonugget['user','information','another_attribute']:secondnugget['user','moreinformation']:extranugget['user','newinformation','newattribute']:newYou can also map the leaves (the actual values at the end of your dictionary) with a callable.>>>t.map_leaves(lambdax:x+" - and there's more!")>>>t.data{'user':{'information':{'attribute':"infonugget - and there's more!",'another_attribute':"secondnugget - and there's more!"},'moreinformation':"extranugget - and there's more!",'newinformation':{'newattribute':"new - and there's more!"}}}All the dictionary helper methods are also provided standalone. There's also an extra one,iter_keysthat iterates over anything that's a key - helpful if there's a bunch of people writing in your codebase and you want to enforce say a regex naming convention on your keys or something.If you're more of the "I need to make sure my API can handle most anything thrown at it!" type, you can define a mapper (using {new_key: [str or sequence]}, try and stick to one, it's prettier, example has both) and throw any dicts you receive into a Tricktionary and map them to the same format. Pretty handy if you need to take in documents in multiple formats, just make sure document x doesn't have different data from document y in the same key path.>>>mapper={'newkey':['user.noninformation.nonattribute',# Doesn't exist'user.information.another_attribute'# Finds],'othernewkey':[['user','noninformation'],# Doesn't exist['user','information']# Finds]}>>>t.map_with_dict(mapper){'newkey':"secondnugget - and there's more!",'othernewkey':{'attribute':"infonugget - and there's more!",'another_attribute':"secondnugget - and there's more!"}}Similar functionality can be found inget_by_seqwhich also takes [str or sequence] and returns val if any of those keys exists.