==============
 Method Cache
==============

cachedIn
========

The `cachedIn` property allows to specify the attribute where to store the
computed value:

    >>> import math
    >>> from zope.cachedescriptors import method

    >>> class Point(object):
    ...
    ...     def __init__(self, x, y):
    ...         self.x, self.y = x, y
    ...
    ...     @method.cachedIn('_cache')
    ...     def distance(self, x, y):
    ...         """Compute the distance"""
    ...         print('computing distance')
    ...         return math.hypot(self.x - x, self.y - y)
    ...
    >>> point = Point(1.0, 2.0)

The value is computed once:

    >>> point.distance(2, 2)
    computing distance
    1.0
    >>> point.distance(2, 2)
    1.0


Using different arguments calculates a new distance:

    >>> point.distance(5, 2)
    computing distance
    4.0
    >>> point.distance(5, 2)
    4.0


The data is stored at the given `_cache` attribute:

    >>> isinstance(point._cache, dict)
    True

    >>> sorted(point._cache.items())
    [(((2, 2), ()), 1.0), (((5, 2), ()), 4.0)]


It is possible to exlicitly invalidate the data:

    >>> point.distance.invalidate(point, 5, 2)
    >>> point.distance(5, 2)
    computing distance
    4.0

Invalidating keys which are not in the cache, does not result in an error:

    >>> point.distance.invalidate(point, 47, 11)

The documentation of the function is preserved (whether through the
instance or the class), allowing Sphinx to extract it::

    >>> print(point.distance.__doc__)
    Compute the distance
    >>> print(point.distance.__name__)
    distance

    >>> print(Point.distance.__doc__)
    Compute the distance
    >>> print(Point.distance.__name__)
    distance

It is possible to pass in a factory for the cache attribute. Create another
Point class:


    >>> class MyDict(dict):
    ...     pass
    >>> class Point(object):
    ...
    ...     def __init__(self, x, y):
    ...         self.x, self.y = x, y
    ...
    ...     @method.cachedIn('_cache', MyDict)
    ...     def distance(self, x, y):
    ...         print('computing distance')
    ...         return math.sqrt((self.x - x)**2 + (self.y - y)**2)
    ...
    >>> point = Point(1.0, 2.0)
    >>> point.distance(2, 2)
    computing distance
    1.0

Now the cache is a MyDict instance:

    >>> isinstance(point._cache, MyDict)
    True
