https://github.com/geyang/waterbear
Waterbear is a simple utility that makes your python dictionary accessible via dot notation.
https://github.com/geyang/waterbear
Last synced: 3 months ago
JSON representation
Waterbear is a simple utility that makes your python dictionary accessible via dot notation.
- Host: GitHub
- URL: https://github.com/geyang/waterbear
- Owner: geyang
- License: other
- Created: 2017-06-14T00:27:41.000Z (almost 8 years ago)
- Default Branch: master
- Last Pushed: 2023-09-03T18:38:00.000Z (over 1 year ago)
- Last Synced: 2025-01-10T12:58:04.354Z (5 months ago)
- Language: Python
- Size: 1.18 MB
- Stars: 2
- Watchers: 0
- Forks: 1
- Open Issues: 2
-
Metadata Files:
- Readme: README
- License: LICENSE.md
Awesome Lists containing this project
README
.. figure:: https://github.com/episodeyang/waterbear/blob/master/figures/waterbear_resized.jpg?raw=true
:width: 355px
:height: 266px
:scale: 50%
:alt: waterbear_is_a_bearwaterbear_is_a_bear
``waterbear``, A Base Classs That Makes Python Dictionary Accessible With The Dot Notation, Recursively and with Default Values
===============================================================================================================================Now introducing the smallest bear! **Waterbear**.
Waterbear makes it easy to use python dictionaries with dot notation!
What does ``Waterbear``:bear: do?
---------------------------------``Waterbear`` is like ``defaultdict`` + ``SimpleNameSpace`` +
``namedtuples``.``Waterbear`` is similar in usage to ``namedtuples`` or ``recordtypes``,
but it is not a tuple or array type but a dictionary. The distinction is
that ``Waterbear`` attributes are accessible via ``key`` strings instead
of index numbers.``Waterbear`` is more similar to ``types.SimpleNamespace``. However, a
major difference is that ``Waterbear`` enables:- setting default values via a ``default_factory`` during instantiation
- all 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 like ``Munch`` has bad support for pythonic idioms.
In this case ``Waterbear`` allows you to:- use ``vars(bear)`` to convert the bear object into a dictionary.
- use ``dict(bear)`` for the same purpose
- use ``print(bear)`` and get a dictionary string
- … all methods that are available in a python ``dict`` objectTODOs
------ ☐ fix class extension usage pattern
- ☐ [STRIKEOUT:merge ``python2.7`` version with ``python3``]
- ☐ [STRIKEOUT:make another package called ``tardigrade``]Installation
------------.. code-block:: python
pip install waterbear
Usage
-----For more usage examples, take a look at the
`test.py `__!There are two classes, the ``Bear`` and the ``DefaultBear``. Default
Bear allows you to pass in a default factory as the first argument.
``Bear`` allows you do do so via a keyword argument ``__default``Example usage below:
.. code-block:: python
# Waterbear is a bear!
from waterbear import Bearwaterbear = Bear(**{"key": 100})
assert waterbear.key == 100, 'now waterbear.key is accessible!'
assert waterbear['key'] == 100, 'item access syntax is also supported!'Similar to ``collection.defaultdict``, there is ``DefaultBear``
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~.. code-block:: python
bear = DefaultBear(None, a=10, b=100)
assert vars(bear) == {'a': 10, 'b': 100}assert bear.does_not_exist is None, "default value works"
DefaultBear like ``defaultdict``
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~You can use the ``DefaultBear`` class and pass in a default factor as
the first parameter... code-block:: python
bear = DefaultBear(tuple, a=10, b=100)
assert bear.does_not_exist is (), "default factory also works!"You can also use it with ``vars``, ``str``, ``print(repr)``, ``dict`` etc.
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~.. code-block:: python
bear = Bear(a=10, b=100)
assert str(bear) == "{'a': 10, 'b': 100}"
assert dir(bear) == ['a', 'b']
assert list(iter(bear)) == ['a', 'b']
assert dict(bear) == {'a': 10, 'b': 100}As Bool in Condition Logic
~~~~~~~~~~~~~~~~~~~~~~~~~~When used in conditional logic, ``Bear`` and ``DefaultBear`` behaves
exactly like an ordinary dictionary!.. code-block:: python
def test_dict_comparison():
bear = Bear()
assert not {}, 'empty dictionary are treated as False value.'
assert not bear, 'bear should be treated as False value too!'Using with Pickle
~~~~~~~~~~~~~~~~~When using with default factories, only non-callables are picklable.
.. code-block:: python
def test_pickle_setstate_getstate():
# create a default bear with a default factory
bear = DefaultBear('hey', a=10, b=100)
pickle_string = pickle.dumps(bear)
bear_reborn = pickle.loads(pickle_string)
assert type(bear_reborn) == DefaultBear
assert vars(bear_reborn) == {'a': 10, 'b': 100}bear = DefaultBear(lambda: 'hey', a=10, b=100)
function_fails = False
try:
pickle.dumps(bear)
except AttributeError as e:
function_fails = True
assert function_failsUsing deepcopy
~~~~~~~~~~~~~~You can just do ``copy.deepcopy(bear)``!
.. code-block:: python
def test_deepcopy():
from copy import deepcopy
original = Bear(a=1, b={'ha': 0})
copy = deepcopy(original)
copy.b.ha += 1
assert copy.b.ha == 1
assert original.b.ha == 0As A Base Class
~~~~~~~~~~~~~~~Waterbear is completely rewritten to play well with class extension!
.. code-block:: python
class ExtendBear(Bear):
@property
def _hidden_stuff(self):
return "._hidden_stuff"@property
def __mangled_stuff(self):
return ".__mangled_stuff"@property
def __dict__(self):
return ".__dict__"e = ExtendBear()
assert e.__dict__ == ".__dict__"
assert e._hidden_stuff == '._hidden_stuff'
assert e._ExtendBear__mangled_stuff == ".__mangled_stuff"Order Preserving SimpleNamespace
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~In tensorflow, you frequently need order preserving namespaces that you
can use for ``sess.run([tensors...``. We built ``OrderedBear`` exactly
for this purpose. It is an extension of the ``types.SimpleNamespace``
class.::
# 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 at
`test.py `__... code-block:: python
test_dict = {
'a': 0,
'b': 1
}# Use spread operators to construct with a dictionary!
test_args = Bear(**test_dict)
assert test_args.a == 0
assert test_args.b == 1
# the value should now be accessible through the key name.
test_args.haha = 0
assert test_args.haha == 0# You can also use a nested dictionary.
test_args.haha = {'a': 1}
assert test_args.haha != {'a': 1}
assert vars(test_args.haha) == {'a': 1}
assert test_args.haha.a == 1
assert test_args.__dict__['haha']['a'] == 1
assert vars(test_args)['haha']['a'] == 1
assert str(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)
assert test_args.__is_recursive == False
assert test_args.a == 0
assert test_args.b == 1
test_args.haha = {'a': 1}
assert test_args.haha['a'] == 1
assert test_args.haha == {'a': 1}# Some other usage patterns
test_args = Bear(**test_dict, **{'ha': 'ha', 'no': 'no'})
assert test_args.ha == 'ha', 'key ha should be ha'To Develop
----------.. code-block:: python
git clone https://github.com/episodeyang/waterbear.git
cd waterbear
make devThis ``make dev`` command should build the wheel and install it in your
current python environment. Take a look at the
`https://github.com/episodeyang/waterbear/blob/master/Makefile `__ for details.**To publish**, first update the version number, then do:
.. code-block:: bash
make publish
\* image credit goes to BBC `waterbear: The Smallest
Bear! `__
😛 |tardigrade|.. |tardigrade| image:: https://github.com/episodeyang/waterbear/blob/master/figures/waterbear_2_resized.jpg?raw=true
:width: 355px
:height: 266px
:scale: 50%