Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ This module provides numerous helper utilities for Python3.X code to add functio
- Progress bars on serial loops and parallel mappings (leveraging the excellent ``tqdm`` library)
- Simple lazy-compute and caching of class properties, including dependency chaining
- Executing Python2 code from within a Python3 program
- In-place modification of functions for common "optimization" tasks
- More intuitive contract decorator (leveraging ``pycontracts``)

Installation
Expand Down
45 changes: 44 additions & 1 deletion docs/source/caching.rst
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
Property Cache
==============

Basic Property
++++++++++++++

In some cases, an object has properties that don't need to be computed until necessary, and once computed are generally static and could just be cached. This could be accomplished using the following simple recipe::

class Obj:
Expand Down Expand Up @@ -100,4 +103,44 @@ This isn't the complete feature set of the decorator, but it's a good initial ta
.. autoclass:: miniutils.caching.CachedProperty
:members:

.. automethod:: __init__
Indexed Property
++++++++++++++++

Even using the above tools, it is non-concise to allow indexing into a property where values are lazily computed.

The ``LazyDictionary`` decorator allows you to write a ``__getitem__`` style property that can be used like a dictionary and has its results cached::

class Primes:
@LazyDictionary()
def is_prime(self, i):
if not isinstance(i, int) or i < 1:
raise ValueError("Can only check if a positive integer is prime")
elif i in [1, 2]:
return True
elif i % 2 == 0:
return False
else:
return all(i % p != 0 for p in range(3, int(math.sqrt(i)) + 1, 2) if self.is_prime[p])

p = Primes()
p.is_prime[5] # True, caches the fact that 1, 2, and 3 are prime
p.is_prime[500] # False, caches all primes up to sqrt(500)
p.is_prime[501] # False, virtually instant since it uses the cached primes used to compute is_prime[500]

The indexing notation is used and preferred to make clear that this decorator only aims to support one hashable argument, and is meant to behave like a dictionary or list. It is not iterable, since the result of that would depend on whatever prior code happened to be executed. Instead, you should iterate through all desired keys, and simply index them; that way, any that need to be re-computed are, and those that can are loaded from cache.

This plugs cleanly into ``CachedProperty``, accepting a list of properties whose values are invalidated when this dictionary is modified. It also supports allowing or disallowing explicit assignment to certain indices::

p = Primes()
p.is_prime[3] = False
p.is_prime[9] # This is now True, since there is no lesser known prime

This is meant to provide a slight additional feature to having a cached dictionary, though honestly it's probably a very small improvement over ``self.is_prime = defaultdict(self._is_prime)``, since it has the additions of invalidating cached properties and making values dependant on their indices.

Values can be explicitly assigned to indices (if ``allow_collection_mutation=True``); assigned values override cached values. Raised ``KeyError``s are cached to prevent re-running indices where failure is known. If an error is not due solely to the index, raise some other error to allow that index to be retried later if some variation to the program's state might allow it to succeed. ``.get(key, default)`` and ``.update(dict)`` are also provided to offer a more dictionary-like interface. A particular object instance will have a :class:`miniutils.caching._LazyDictionary` instance which provides its caching, though the decorated function is once again replaced with a simple ``@property``.

.. autoclass:: miniutils.caching.LazyDictionary
:members:

.. autoclass:: miniutils.caching._LazyDictionary
:members:
4 changes: 2 additions & 2 deletions docs/source/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -60,9 +60,9 @@
# built documents.
#
# The short X.Y version.
version = '0.0.1'
version = '1.0'
# The full version, including alpha/beta/rc tags.
release = '0.0.1'
release = '1.0.1'

# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.
Expand Down
1 change: 0 additions & 1 deletion docs/source/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@ Welcome to miniutils's documentation!
progress_bar
caching
python2
pragma
misc
api

Expand Down
Loading