cache

The cache module.

class nutils.cache.Wrapper(func)

Bases: object

function decorator that caches results by arguments

__weakref__

list of weak references to the object (if defined)

class nutils.cache.WrapperCache

Bases: object

maintains a cache for Wrapper instances

__weakref__

list of weak references to the object (if defined)

nutils.cache.enable(cachedir)

Enable cacheing and set the cache directory to cachedir. Affects functions decorated with function() and subclasses of Recursion.

nutils.cache.disable()

Disable cacheing. Affects functions decorated with function() and subclasses of Recursion.

nutils.cache.function(func=None, *, version=0)

Decorator to wrap a function func with a memoizing callable. It is assumed that func computes its return value based strictly on the arguments. In other words: calling func with the same arguments repeatedly, should produce the same return value. All arguments passed to the decorator should be hashable (by nutils.types.nutils_hash()).

Memoization is controlled by the context managers enable() and disable(). If inside an enable() context, memoization is enabled: The first time the decorator is called with a unique set of arguments, the decorator calls func and stores the result on disk in the directory specified by the argument to enable(); when the decorator is called with the same arguments, the result is retrieved from the cache. If inside a disable() context, the decorator calls func directly, bypassing the cache. Note that memoization is off by default.

Parameters:
  • func (callable) – The function to be memoized.
  • version (int) –

    Optional version number of func. Increment this if the behavior of func is changed. The decorator can be applied as follows:

    >>> @function(version=1)
    ... def f(x):
    ...   return x
    
Returns:

A memoized version of func.

Return type:

callable

class nutils.cache.Recursion(*args, **kwargs)

Bases: nutils.types.Immutable

Base class for memoized iterators with fixed recursion. This class describes iterators of the form

\[x_i = f(x_{i-1}, x_{i-2}, \ldots, x_{i-n})\]

where \(n\) is the recursion length. The iterator is defined by the abstract resume() method. The method takes a single parameter history: a list of the last length items, or less if the iteration is resumed after less than length iterations. The method should proceed with yielding the remaining items. The resume() method should follow above definition of the recursion and the generator \(f\) should be based strictly on the initialization arguments of the subclass. Failing to do so will lead to unpredictable behavior if memoization is enabled. As this class bases nutils.types.Immutable, all initialization arguments should be hashable (by nutils.types.nutils_hash()).

The recursion length should be passed as keyword argument when defining the class. For example:

class Subclass(Recursion, length=1):
  def resume(self, history):
    ...

Memoization is controlled by the context managers enable() and disable(). If inside an enable() context, memoization is enabled: All cached iterations are retrieved from disk and are yielded; if iteration continues, the resume() method is called to produce the remaining iterations. If inside a disable() context, the memoization is disabled and the resume() method is called immediately with empty history.

Note that this class is iterable, but is not an iterator. Calling iter() on an instance of this class, e.g. implicitly in a for statement, the returned iterator always starts from scratch.

Examples

The Fibonacc sequence

\[f(x_{i-1}, x_{i-2}) := x_{i-1} + x_{i-2},\]

with variable seed values \(x_0\) and \(x_1\) can be implemented as follows.

>>> class Fibonacci(Recursion, length=2):
...   def __init__(self, x0, x1):
...     self.x0 = x0
...     self.x1 = x1
...   def resume(self, history):
...     if len(history) == 0:
...       yield self.x0
...       history.append(self.x0)
...     if len(history) == 1:
...       yield self.x1
...       history.append(self.x1)
...     while True:
...       value = history[-2] + history[-1]
...       yield value
...       history = history[-1], value
...
>>> f = iter(Fibonacci(1, 1))
>>> for i in range(6):
...   next(f)
1
1
2
3
5
8
resume(self, history)

Resume recursion from history.

Note

This function is abstract.