.. figure:: https://github.com/episodeyang/waterbear/blob/master/figures/waterbear_resized.jpg?raw=true
:width: 355px
:height: 266px
:scale: 50%
:alt: waterbear_is_a_bear
waterbear_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 recursively
Now 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`` object
TODOs
-----
- ☐ 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 <https://github.com/episodeyang/waterbear/blob/master/waterbear/test_waterbear.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 Bear
waterbear = 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_fails
Using 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 == 0
As 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 <https://github.com/episodeyang/waterbear/blob/master/waterbear/test_waterbear.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 dev
This ``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 <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! <http://www.bbc.com/earth/story/20150313-the-toughest-animals-on-earth>`__
😛 |tardigrade|
.. |tardigrade| image:: https://github.com/episodeyang/waterbear/blob/master/figures/waterbear_2_resized.jpg?raw=true
:width: 355px
:height: 266px
:scale: 50%
Raw data
{
"_id": null,
"home_page": "https://github.com/episodeyang/waterbear",
"name": "waterbear",
"maintainer": "",
"docs_url": null,
"requires_python": "",
"maintainer_email": "",
"keywords": "waterbear,dict,dot-notation",
"author": "Ge Yang",
"author_email": "yangge1987@gmail.com",
"download_url": "",
"platform": null,
"description": ".. figure:: https://github.com/episodeyang/waterbear/blob/master/figures/waterbear_resized.jpg?raw=true\n :width: 355px\n :height: 266px\n :scale: 50%\n :alt: waterbear_is_a_bear\n\n waterbear_is_a_bear\n\n``waterbear``, A Base Classs That Makes Python Dictionary Accessible With The Dot Notation, Recursively and with Default Values\n===============================================================================================================================\n\nNow introducing the smallest bear! **Waterbear**.\n\nWaterbear makes it easy to use python dictionaries with dot notation!\n\nWhat does ``Waterbear``:bear: do?\n---------------------------------\n\n``Waterbear`` is like ``defaultdict`` + ``SimpleNameSpace`` +\n``namedtuples``.\n\n``Waterbear`` is similar in usage to ``namedtuples`` or ``recordtypes``,\nbut it is not a tuple or array type but a dictionary. The distinction is\nthat ``Waterbear`` attributes are accessible via ``key`` strings instead\nof index numbers.\n\n``Waterbear`` is more similar to ``types.SimpleNamespace``. However, a\nmajor difference is that ``Waterbear`` enables:\n\n- setting default values via a ``default_factory`` during instantiation\n- all attributes are recognized by IDE\u2019s static type-checking so they\n have auto-completion without having to be used first.\n- work recursively\n\nNow with all of these three, there isn\u2019t an alternative solution\navailable. libraries like ``Munch`` has bad support for pythonic idioms.\nIn this case ``Waterbear`` allows you to:\n\n- use ``vars(bear)`` to convert the bear object into a dictionary.\n- use ``dict(bear)`` for the same purpose\n- use ``print(bear)`` and get a dictionary string\n- \u2026 all methods that are available in a python ``dict`` object\n\nTODOs\n-----\n\n- \u2610 fix class extension usage pattern\n- \u2610 [STRIKEOUT:merge ``python2.7`` version with ``python3``]\n- \u2610 [STRIKEOUT:make another package called ``tardigrade``]\n\nInstallation\n------------\n\n.. code-block:: python\n\n pip install waterbear\n\nUsage\n-----\n\nFor more usage examples, take a look at the\n`test.py <https://github.com/episodeyang/waterbear/blob/master/waterbear/test_waterbear.py>`__!\n\nThere are two classes, the ``Bear`` and the ``DefaultBear``. Default\nBear allows you to pass in a default factory as the first argument.\n``Bear`` allows you do do so via a keyword argument ``__default``\n\nExample usage below:\n\n.. code-block:: python\n\n # Waterbear is a bear!\n from waterbear import Bear\n\n waterbear = Bear(**{\"key\": 100})\n assert waterbear.key == 100, 'now waterbear.key is accessible!'\n assert waterbear['key'] == 100, 'item access syntax is also supported!'\n\nSimilar to ``collection.defaultdict``, there is ``DefaultBear``\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\n.. code-block:: python\n\n bear = DefaultBear(None, a=10, b=100)\n assert vars(bear) == {'a': 10, 'b': 100}\n\n assert bear.does_not_exist is None, \"default value works\"\n\nDefaultBear like ``defaultdict``\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nYou can use the ``DefaultBear`` class and pass in a default factor as\nthe first parameter.\n\n.. code-block:: python\n\n bear = DefaultBear(tuple, a=10, b=100)\n assert bear.does_not_exist is (), \"default factory also works!\"\n\nYou can also use it with ``vars``, ``str``, ``print(repr)``, ``dict`` etc.\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\n.. code-block:: python\n\n bear = Bear(a=10, b=100)\n assert str(bear) == \"{'a': 10, 'b': 100}\"\n assert dir(bear) == ['a', 'b']\n assert list(iter(bear)) == ['a', 'b']\n assert dict(bear) == {'a': 10, 'b': 100}\n\nAs Bool in Condition Logic\n~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nWhen used in conditional logic, ``Bear`` and ``DefaultBear`` behaves\nexactly like an ordinary dictionary!\n\n.. code-block:: python\n\n def test_dict_comparison():\n bear = Bear()\n assert not {}, 'empty dictionary are treated as False value.'\n assert not bear, 'bear should be treated as False value too!'\n\nUsing with Pickle\n~~~~~~~~~~~~~~~~~\n\nWhen using with default factories, only non-callables are picklable.\n\n.. code-block:: python\n\n def test_pickle_setstate_getstate():\n # create a default bear with a default factory\n bear = DefaultBear('hey', a=10, b=100)\n pickle_string = pickle.dumps(bear)\n bear_reborn = pickle.loads(pickle_string)\n assert type(bear_reborn) == DefaultBear\n assert vars(bear_reborn) == {'a': 10, 'b': 100}\n\n bear = DefaultBear(lambda: 'hey', a=10, b=100)\n function_fails = False\n try:\n pickle.dumps(bear)\n except AttributeError as e:\n function_fails = True\n assert function_fails\n\nUsing deepcopy\n~~~~~~~~~~~~~~\n\nYou can just do ``copy.deepcopy(bear)``!\n\n.. code-block:: python\n\n def test_deepcopy():\n from copy import deepcopy\n original = Bear(a=1, b={'ha': 0})\n copy = deepcopy(original)\n copy.b.ha += 1\n assert copy.b.ha == 1\n assert original.b.ha == 0\n\nAs A Base Class\n~~~~~~~~~~~~~~~\n\nWaterbear is completely rewritten to play well with class extension!\n\n.. code-block:: python\n\n class ExtendBear(Bear):\n @property\n def _hidden_stuff(self):\n return \"._hidden_stuff\"\n\n @property\n def __mangled_stuff(self):\n return \".__mangled_stuff\"\n\n @property\n def __dict__(self):\n return \".__dict__\"\n\n e = ExtendBear()\n assert e.__dict__ == \".__dict__\"\n assert e._hidden_stuff == '._hidden_stuff'\n assert e._ExtendBear__mangled_stuff == \".__mangled_stuff\"\n\nOrder Preserving SimpleNamespace\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nIn tensorflow, you frequently need order preserving namespaces that you\ncan use for ``sess.run([tensors...``. We built ``OrderedBear`` exactly\nfor this purpose. It is an extension of the ``types.SimpleNamespace``\nclass.\n\n::\n\n # First declare the typings (namespace) for your model\n class Reporting:\n loss=None\n entropy=None\n mean_kl=None\n\n # Now, you can instantiate this with new values\n\n ... inside model\n\n r = Reporting(entropy=-5, loss=1)\n # Notice that 1. we are putting values in out-of-order, and 2. We are missing `mean_kl` in our construction.\n\n tems = r.items()\n assert items[0] == ('loss', 1), 'order follows class declaration.'\n assert items[1] == ('entropy', -5), 'entropy goes after loss even though this is the second atrribute'\n assert items[2] == ('mean_kl', None), 'undefined falls back to the default'\n\n values = r.values()\n assert values[0] == 1, 'order follows class declaration.'\n assert values[1] == -5, 'entropy goes after loss even though this is the second atrribute'\n assert values[2] == None, 'undefined falls back to the default'\n\n keys = r.keys()\n assert keys[0] == 'loss', 'order follows class declaration.'\n assert keys[1] == 'entropy', 'entropy goes after loss even though this is the second atrribute'\n assert keys[2] == 'mean_kl', 'undefined falls back to the default'\n\nMore Usages Could Be Found in The Tests!\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nFor more usage examples, take a look at\n`test.py <https://github.com/episodeyang/waterbear/blob/master/waterbear/test_waterbear.py>`__.\n\n.. code-block:: python\n\n test_dict = {\n 'a': 0,\n 'b': 1\n }\n\n # Use spread operators to construct with a dictionary!\n test_args = Bear(**test_dict)\n assert test_args.a == 0\n assert test_args.b == 1\n # the value should now be accessible through the key name.\n test_args.haha = 0\n assert test_args.haha == 0\n\n\n # You can also use a nested dictionary.\n test_args.haha = {'a': 1}\n assert test_args.haha != {'a': 1}\n assert vars(test_args.haha) == {'a': 1}\n assert test_args.haha.a == 1\n assert test_args.__dict__['haha']['a'] == 1\n assert vars(test_args)['haha']['a'] == 1\n assert str(test_args) == \"{'a': 0, 'b': 1, 'haha': {'a': 1}}\", \\\n 'test_args should be this value \"{\\'a\\': 0, \\'b\\': 1, \\'haha\\': {\\'a\\': 1}}\"'\n\n # To set recursion to false, use this `__recursive` parameter.\n test_args = Bear(__recursive=False, **test_dict)\n assert test_args.__is_recursive == False\n assert test_args.a == 0\n assert test_args.b == 1\n test_args.haha = {'a': 1}\n assert test_args.haha['a'] == 1\n assert test_args.haha == {'a': 1}\n\n # Some other usage patterns\n test_args = Bear(**test_dict, **{'ha': 'ha', 'no': 'no'})\n assert test_args.ha == 'ha', 'key ha should be ha'\n\nTo Develop\n----------\n\n.. code-block:: python\n\n git clone https://github.com/episodeyang/waterbear.git\n cd waterbear\n make dev\n\nThis ``make dev`` command should build the wheel and install it in your\ncurrent python environment. Take a look at the\n`https://github.com/episodeyang/waterbear/blob/master/Makefile <https://github.com/episodeyang/waterbear/blob/master/Makefile>`__ for details.\n\n**To publish**, first update the version number, then do:\n\n.. code-block:: bash\n\n make publish\n\n\\* image credit goes to BBC `waterbear: The Smallest\nBear! <http://www.bbc.com/earth/story/20150313-the-toughest-animals-on-earth>`__\n\ud83d\ude1b |tardigrade|\n\n.. |tardigrade| image:: https://github.com/episodeyang/waterbear/blob/master/figures/waterbear_2_resized.jpg?raw=true\n :width: 355px\n :height: 266px\n :scale: 50%\n\n\n",
"bugtrack_url": null,
"license": "",
"summary": "A utility that makes it easy to use dot notation with python dictionaries",
"version": "2.6.8",
"project_urls": {
"Homepage": "https://github.com/episodeyang/waterbear"
},
"split_keywords": [
"waterbear",
"dict",
"dot-notation"
],
"urls": [
{
"comment_text": "",
"digests": {
"blake2b_256": "a63cf1b41d47e038e922ae751d2dbddea1c5744ad79ab14d9bbec983d85afd0f",
"md5": "6e2e92df8d232f37ad255ab23fa581e0",
"sha256": "e7c42bae01f4287eedde5bb71ab4671e93ca10edf3ebfba09a432ac4bace3f59"
},
"downloads": -1,
"filename": "waterbear-2.6.8-py3-none-any.whl",
"has_sig": false,
"md5_digest": "6e2e92df8d232f37ad255ab23fa581e0",
"packagetype": "bdist_wheel",
"python_version": "py3",
"requires_python": null,
"size": 9791,
"upload_time": "2023-09-03T18:38:06",
"upload_time_iso_8601": "2023-09-03T18:38:06.505337Z",
"url": "https://files.pythonhosted.org/packages/a6/3c/f1b41d47e038e922ae751d2dbddea1c5744ad79ab14d9bbec983d85afd0f/waterbear-2.6.8-py3-none-any.whl",
"yanked": false,
"yanked_reason": null
}
],
"upload_time": "2023-09-03 18:38:06",
"github": true,
"gitlab": false,
"bitbucket": false,
"codeberg": false,
"github_user": "episodeyang",
"github_project": "waterbear",
"travis_ci": false,
"coveralls": false,
"github_actions": false,
"lcname": "waterbear"
}