Package: waterbear
waterbear_is_a_bearwaterbear, A Base Classs That Makes Python Dictionary Accessible With The Dot Notation, Recursively and with Default ValuesNow introducing the smallest bear!Waterbear.Waterbear makes it easy to use python dictionaries with dot notation!What doesWaterbear:bear: do?Waterbearis likedefaultdict+SimpleNameSpace+namedtuples.Waterbearis similar in usage tonamedtuplesorrecordtypes,
but it is not a tuple or array type but a dictionary. The distinction is
thatWaterbearattributes are accessible viakeystrings instead
of index numbers.Waterbearis more similar totypes.SimpleNamespace. However, a
major difference is thatWaterbearenables:setting default values via adefault_factoryduring instantiationall attributes are recognized by IDE’s static type-checking so they
have auto-completion without having to be used first.work recursivelyNow with all of these three, there isn’t an alternative solution
available. libraries likeMunchhas bad support for pythonic idioms.
In this caseWaterbearallows you to:usevars(bear)to convert the bear object into a dictionary.usedict(bear)for the same purposeuseprint(bear)and get a dictionary string… all methods that are available in a pythondictobjectTODOs☐ fix class extension usage pattern☐ [STRIKEOUT:mergepython2.7version withpython3]☐ [STRIKEOUT:make another package calledtardigrade]InstallationpipinstallwaterbearUsageFor more usage examples, take a look at thetest.py!There are two classes, theBearand theDefaultBear. Default
Bear allows you to pass in a default factory as the first argument.Bearallows you do do so via a keyword argument__defaultExample usage below:# Waterbear is a bear!fromwaterbearimportBearwaterbear=Bear(**{"key":100})assertwaterbear.key==100,'now waterbear.key is accessible!'assertwaterbear['key']==100,'item access syntax is also supported!'Similar tocollection.defaultdict, there isDefaultBearbear=DefaultBear(None,a=10,b=100)assertvars(bear)=={'a':10,'b':100}assertbear.does_not_existisNone,"default value works"DefaultBear likedefaultdictYou can use theDefaultBearclass and pass in a default factor as
the first parameter.bear=DefaultBear(tuple,a=10,b=100)assertbear.does_not_existis(),"default factory also works!"You can also use it withvars,str,print(repr),dictetc.bear=Bear(a=10,b=100)assertstr(bear)=="{'a': 10, 'b': 100}"assertdir(bear)==['a','b']assertlist(iter(bear))==['a','b']assertdict(bear)=={'a':10,'b':100}As Bool in Condition LogicWhen used in conditional logic,BearandDefaultBearbehaves
exactly like an ordinary dictionary!deftest_dict_comparison():bear=Bear()assertnot{},'empty dictionary are treated as False value.'assertnotbear,'bear should be treated as False value too!'Using with PickleWhen using with default factories, only non-callables are picklable.deftest_pickle_setstate_getstate():# create a default bear with a default factorybear=DefaultBear('hey',a=10,b=100)pickle_string=pickle.dumps(bear)bear_reborn=pickle.loads(pickle_string)asserttype(bear_reborn)==DefaultBearassertvars(bear_reborn)=={'a':10,'b':100}bear=DefaultBear(lambda:'hey',a=10,b=100)function_fails=Falsetry:pickle.dumps(bear)exceptAttributeErrorase:function_fails=Trueassertfunction_failsUsing deepcopyYou can just docopy.deepcopy(bear)!deftest_deepcopy():fromcopyimportdeepcopyoriginal=Bear(a=1,b={'ha':0})copy=deepcopy(original)copy.b.ha+=1assertcopy.b.ha==1assertoriginal.b.ha==0As A Base ClassWaterbear is completely rewritten to play well with class extension!classExtendBear(Bear):@propertydef_hidden_stuff(self):return"._hidden_stuff"@propertydef__mangled_stuff(self):return".__mangled_stuff"@propertydef__dict__(self):return".__dict__"e=ExtendBear()asserte.__dict__==".__dict__"asserte._hidden_stuff=='._hidden_stuff'asserte._ExtendBear__mangled_stuff==".__mangled_stuff"Order Preserving SimpleNamespaceIn tensorflow, you frequently need order preserving namespaces that you
can use forsess.run([tensors.... We builtOrderedBearexactly
for this purpose. It is an extension of thetypes.SimpleNamespaceclass.# First declare the typings (namespace) for your model
class Reporting:
    loss=None
    entropy=None
    mean_kl=None

# Now, you can instantiate this with new values

... inside model

    r = Reporting(entropy=-5, loss=1)
    # Notice that 1. we are putting values in out-of-order, and 2. We are missing `mean_kl` in our construction.

    tems = r.items()
    assert items[0] == ('loss', 1), 'order follows class declaration.'
    assert items[1] == ('entropy', -5), 'entropy goes after loss even though this is the second atrribute'
    assert items[2] == ('mean_kl', None), 'undefined falls back to the default'

    values = r.values()
    assert values[0] == 1, 'order follows class declaration.'
    assert values[1] == -5, 'entropy goes after loss even though this is the second atrribute'
    assert values[2] == None, 'undefined falls back to the default'

    keys = r.keys()
    assert keys[0] == 'loss', 'order follows class declaration.'
    assert keys[1] == 'entropy', 'entropy goes after loss even though this is the second atrribute'
    assert keys[2] == 'mean_kl', 'undefined falls back to the default'More Usages Could Be Found in The Tests!For more usage examples, take a look attest.py.test_dict={'a':0,'b':1}# Use spread operators to construct with a dictionary!test_args=Bear(**test_dict)asserttest_args.a==0asserttest_args.b==1# the value should now be accessible through the key name.test_args.haha=0asserttest_args.haha==0# You can also use a nested dictionary.test_args.haha={'a':1}asserttest_args.haha!={'a':1}assertvars(test_args.haha)=={'a':1}asserttest_args.haha.a==1asserttest_args.__dict__['haha']['a']==1assertvars(test_args)['haha']['a']==1assertstr(test_args)=="{'a': 0, 'b': 1, 'haha': {'a': 1}}",\'test_args should be this value "{\'a\': 0,\'b\': 1,\'haha\': {\'a\': 1}}"'# To set recursion to false, use this `__recursive` parameter.test_args=Bear(__recursive=False,**test_dict)asserttest_args.__is_recursive==Falseasserttest_args.a==0asserttest_args.b==1test_args.haha={'a':1}asserttest_args.haha['a']==1asserttest_args.haha=={'a':1}# Some other usage patternstest_args=Bear(**test_dict,**{'ha':'ha','no':'no'})asserttest_args.ha=='ha','key ha should be ha'To Developgitclonehttps://github.com/episodeyang/waterbear.gitcdwaterbearmakedevThismake devcommand should build the wheel and install it in your
current python environment. Take a look at thehttps://github.com/episodeyang/waterbear/blob/master/Makefilefor details.To publish, first update the version number, then do:makepublish* image credit goes to BBCwaterbear: The Smallest
Bear!😛