Skip to main content

vivodict

https://img.shields.io/pypi/v/vivodict.svg https://img.shields.io/travis/somada141/vivodict.svg Documentation Status

This package provides a simple implementation of an auto-vivified Python dict, i.e., a dictionary where accessing a missing key doesn’t raise the standard KeyError exception but instead implicitly creates and returns an empty auto-vivified dict under that key.

Features

  • Auto-vivified VivoDict class derived from the standard Python dict class (no third-party dependencies).

  • Auto-vivification of arbitrarily-nested dict objects.

  • Convenience methods for flatten, replace, and apply operations.

  • Free software: MIT license

  • Documentation: https://vivodict.readthedocs.io.

Motivation

My primary motivation for developing this package is because it contained a piece of code I kept copy-pasting like a bloody caveman between projects.

My typical use-cases for this code include:

  • Wrap the decoded JSON dict from crummy APIs without a schema that just decide to drop keys for which the values are null resulting in code with nested if "key" in result:. This allowed me to either retrieve the value if it was there or at least arriving at an empty dict which evaluates to False when mapping their half-formed data to my own data-structures.

  • Create arbitrarily-nested dictionaries of code stats that I can keep organized as I like while using in the code and then quickly flatten to a Graphite compatible format prior to posting them to … well Graphite.

Basic Usage

This would be the typical Python dict behaviour when accessing a missing key:

>>> d = {"a": 1, "b": 2}
>>> d["a"]
1
>>> d["missing"]
---------------------------------------------------------------------------
KeyError                                  Traceback (most recent call last)
<ipython-input-3-d4f58b57b715> in <module>()
----> 1 d["missing"]

KeyError: 'missing'

While if we were using a VivoDict, then upon accessing a missing key we would be provided with an implicitly created empty VivoDict as such:

>>> from vivodict import VivoDict
>>> d = VivoDict.vivify({"a": 1, "b": 2})
>>> d["a"]
1
>>> d["missing"]
{}

Now, while the above doesn’t seem to offer anything a simple try-except or a if "key" not in d wouldn’t offer, the VivoDict becomes useful when dealing with arbitrarily nested dictionaries where there may be several levels of missing keys. For example:

>>> from vivodict import VivoDict
>>> d = VivoDict({"a": 1, "b": {"c": 2}, "d": {"e": {"f": 3}}})
>>> d["a"]
1
>>> d["b"]["c"]
2
>>> d["d"]["e"]["f"]
3
>>> d["i"]["am"]["missing"]["eh"] = 4
>>> d
{'a': 1,
'b': {'c': 2},
'd': {'e': {'f': 3}},
'i': {'am': {'missing': {'eh': 4}}}}

So, as can be seen, having auto-vivification allows one to nest keys and values to whatever degree.

Convenience Functions

In addition to the above, a few basic convenience methods have been built into the VivoDict class, mostly cause they make my life easier and lazier.

flatten

As I mentioned prior one of my typical use-cases for vivodict is using it to store nested metrics which I then post to Graphite via simple HTTP requests.

Graphite, however, bases its structure on . delimited names where anything preceding a . is considered to be a metric folder with the last token being the metric itself.

Thus, I needed a quick way to flatten a nested dict into a Graphite compatible version.

The flatten method does exactly that:

>>> d = VivoDict.vivify({"a": 1, "b": {"c": 2}, "d": {"e": {"f": 3}}})
>>> d.flatten()
{'a': 1, 'b.c': 2, 'd.e.f': 3}

replace

Following the same premise as with flatten I needed to quickly ‘reset’ my metrics back to 0 between posting cycles.

Hence, replace will replace all ‘leaf’ node values in what is essentially a tree with a given value:

>>> d = VivoDict.vivify({"a": 1, "b": {"c": 2}, "d": {"e": {"f": 3}}})
>>> d.replace(replace_with=0)
>>> d
{'a': 0, 'b': {'c': 0}, 'd': {'e': {'f': 0}}}

Should you need to maintain an original copy I’d suggest you use the copy package and its deepcopy function (cause Python passes by reference) as such:

>>> import copy
>>> original = VivoDict.vivify({"a": 1, "b": {"c": 2}, "d": {"e": {"f": 3}}})
>>> modified = copy.deepcopy(original)
>>> modified.replace(replace_with=0)
>>> original
{'a': 1, 'b': {'c': 2}, 'd': {'e': {'f': 3}}}
>>> modified
{'a': 0, 'b': {'c': 0}, 'd': {'e': {'f': 0}}}

apply

Lastly, I often had to modify all values through a given function, typically divide them by a number of observation for average metrics which can be easily done through the apply method which can take any callable as an argument and replace the original value with its return-value:

>>> d = VivoDict.vivify({"a": 1, "b": {"c": 2}, "d": {"e": {"f": 3}}})
>>> def double(value):
>>>     return value * 2
>>> d.apply(double)
>>> d
{'a': 2, 'b': {'c': 4}, 'd': {'e': {'f': 6}}}
>>> d.apply(lambda value: value / 2)
{'a': 1, 'b': {'c': 2}, 'd': {'e': {'f': 3}}}

History

0.3.1 (2017-07-23)

  • README.rst: Fixed minor formatting typo.

0.3.0 (2017-07-23)

  • Cleanup the docos and removed a bunch of the unnecessary stuff.

0.2.0 (2017-07-23)

  • Added more unit-tests and improved docstrings.

0.1.1 (2017-07-23)

  • Fixed issues with the Python dependencies.

0.1.0 (2017-07-23)

  • First release on PyPI.

Release files for vivodict 0.3.1

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for vivodict 0.3.1
File Size Uploaded
vivodict-0.3.1.tar.gz 14.8 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for vivodict 0.3.1
File Interpreter ABI Platform
vivodict-0.3.1-py2.py3-none-any.whl Python 2, Python 3 none any Details

Total release size: 25.1 kB

Release files / vivodict-0.3.1.tar.gz

Download URL vivodict-0.3.1.tar.gz
Size 14.8 kB
Tags Source
SHA-256 checksum
How to use checksums
1e4c0d2001bb424e514cab14ea56d31a4d770958973bdf7366ff90dfb9bc81a1
BLAKE2b-256 checksum
How to use checksums
3af52233f19ff549273613c0d7a12947e8d73739007fb2dcf90625e78686ab41
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No

Release files / vivodict-0.3.1-py2.py3-none-any.whl

Download URL vivodict-0.3.1-py2.py3-none-any.whl
Size 10.3 kB
Tags Python 2 Python 3
SHA-256 checksum
How to use checksums
4bab31869bdcf005a7e5258d71109e48940703d71af3fd7d02eedda828e05c5c
BLAKE2b-256 checksum
How to use checksums
670257ade8fd3d8984a3cee6c02adae4a0c287cdf1445a69684f3c3c73be4076
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No

Release history Release notifications | RSS feed

This release

0.3.1 This release

2 release files

0.3.0

2 release files

0.2.0

2 release files

0.1.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page