CMD + K

cached-property

Community

A decorator for caching properties in classes.

Installation

To install this package, run one of the following:

Pip
$pip install -i https://pypi.anaconda.org/rolandohub/simple cached-property

Usage Tracking

1.2.0
1 / 8 versions selected
Downloads (Last 6 months): 0

Description

===============================

cached-property

.. image:: https://badge.fury.io/py/cached-property.png :target: http://badge.fury.io/py/cached-property

.. image:: https://travis-ci.org/pydanny/cached-property.png?branch=master :target: https://travis-ci.org/pydanny/cached-property

.. image:: https://pypip.in/d/cached-property/badge.png :target: https://pypi.python.org/pypi/cached-property

A decorator for caching properties in classes.

Why?

  • Makes caching of time or computational expensive properties quick and easy.
  • Because I got tired of copy/pasting this code from non-web project to non-web project.
  • I needed something really simple that worked in Python 2 and 3.

How to use it

Let's define a class with an expensive property. Every time you stay there the price goes up by $50!

.. code-block:: python

class Monopoly(object):

    def __init__(self):
        self.boardwalk_price = 500

    @property
    def boardwalk(self):
        # In reality, this might represent a database call or time 
        # intensive task like calling a third-party API.
        self.boardwalk_price += 50
        return self.boardwalk_price

Now run it:

.. code-block:: python

>>> monopoly = Monopoly()
>>> monopoly.boardwalk
550
>>> monopoly.boardwalk
600

Let's convert the boardwalk property into a cached_property.

.. code-block:: python

from cached_property import cached_property

class Monopoly(object):

    def __init__(self):
        self.boardwalk_price = 500

    @cached_property
    def boardwalk(self):
        # Again, this is a silly example. Don't worry about it, this is
        #   just an example for clarity.
        self.boardwalk_price += 50
        return self.boardwalk_price

Now when we run it the price stays at $550.

.. code-block:: python

>>> monopoly = Monopoly()
>>> monopoly.boardwalk
550
>>> monopoly.boardwalk
550
>>> monopoly.boardwalk
550

Why doesn't the value of monopoly.boardwalk change? Because it's a cached property!

Invalidating the Cache

Results of cached functions can be invalidated by outside forces. Let's demonstrate how to force the cache to invalidate:

.. code-block:: python

>>> monopoly = Monopoly()
>>> monopoly.boardwalk
550
>>> monopoly.boardwalk
550
>>> # invalidate the cache
>>> del monopoly['boardwalk']
>>> # request the boardwalk property again
>>> monopoly.boardwalk
600
>>> monopoly.boardwalk
600

Working with Threads

What if a whole bunch of people want to stay at Boardwalk all at once? This means using threads, which unfortunately causes problems with the standard cached_property. In this case, switch to using the threaded_cached_property:

.. code-block:: python

import threading

from cached_property import threaded_cached_property

class Monopoly(object):

    def __init__(self):
        self.boardwalk_price = 500
        self.lock = threading.Lock()

    @threaded_cached_property
    def boardwalk(self):
        """threaded_cached_property is really nice for when no one waits
            for other people to finish their turn and rudely start rolling
            dice and moving their pieces."""

        sleep(1)
        # Need to guard this since += isn't atomic.
        with self.lock:
            self.boardwalk_price += 50
        return self.boardwalk_price

Now use it:

.. code-block:: python

>>> from threading import Thread
>>> from monopoly import Monopoly
>>> monopoly = Monopoly()
>>> threads = []
>>> for x in range(10):
>>>     thread = Thread(target=lambda: monopoly.boardwalk)
>>>     thread.start()
>>>     threads.append(thread)

>>> for thread in threads:
>>>     thread.join()

>>> self.assertEqual(m.boardwalk, 550)

Timing out the cache

Sometimes you want the price of things to reset after a time. Use the ttl versions of cached_property and threaded_cached_property.

.. code-block:: python

import random
from cached_property import cached_property_with_ttl

class Monopoly(object):

    @cached_property_with_ttl(ttl=5) # cache invalidates after 5 seconds
    def dice(self):
        # I dare the reader to implement a game using this method of 'rolling dice'.
        return random.randint(2,12)

Now use it:

.. code-block:: python

>>> monopoly = Monopoly()
>>> monopoly.dice
10
>>> monopoly.dice
10
>>> from time import sleep
>>> sleep(6) # Sleeps long enough to expire the cache
>>> monopoly.dice
3
>>> monopoly.dice
3

Note: The ttl tools do not reliably allow the clearing of the cache. This is why they are broken out into seperate tools. See https://github.com/pydanny/cached-property/issues/16.

Credits

  • Pip, Django, Werkzueg, Bottle, Pyramid, and Zope for having their own implementations. This package uses an implementation that matches the Bottle version.
  • Reinout Van Rees for pointing out the cached_property decorator to me.
  • My awesome wife @audreyr_ who created cookiecutter_, which meant rolling this out took me just 15 minutes.
  • @tinche for pointing out the threading issue and providing a solution.
  • @bcho for providing the time-to-expire feature

.. _@audreyr: https://github.com/audreyr .. _cookiecutter: https://github.com/audreyr/cookiecutter

History

1.2.0 (2015-04-28) ++++++++++++++++++

  • Overall code and test refactoring, thanks to @gsakkis
  • Allow the del statement for resetting cached properties with ttl instead of del obj._cache[attr], thanks to @gsakkis.
  • Uncovered a bug in PyPy, https://bitbucket.org/pypy/pypy/issue/2033/attributeerror-object-attribute-is-read, thanks to @gsakkis
  • Fixed threadedcachedpropertywithttl to actually be thread-safe, thanks to @gsakkis

1.1.0 (2015-04-04) ++++++++++++++++++

  • Regression: As the cache was not always clearing, weve broken out the time to expire feature to its own set of specific tools, thanks to @pydanny
  • Fixed typo in README, thanks to @zoidbergwill

1.0.0 (2015-02-13) ++++++++++++++++++

  • Added timed to expire feature to cached_property decorator.
  • Backwards incompatiblity: Changed del monopoly.boardwalk to del monopoly['boardwalk'] in order to support the new TTL feature.

0.1.5 (2014-05-20) ++++++++++++++++++

  • Added threading support with new threaded_cached_property decorator
  • Documented cache invalidation
  • Updated credits
  • Sourced the bottle implementation

0.1.4 (2014-05-17) ++++++++++++++++++

  • Fix the dang-blarged py_modules argument.

0.1.3 (2014-05-17) ++++++++++++++++++

  • Removed import of package into setup.py

0.1.2 (2014-05-17) ++++++++++++++++++

  • Documentation fixes. Not opening up a RTFD instance for this because it's so simple to use.

0.1.1 (2014-05-17) ++++++++++++++++++

  • setup.py fix. Whoops!

0.1.0 (2014-05-17) ++++++++++++++++++

  • First release on PyPI.

About

Summary

A decorator for caching properties in classes.

Last Updated

May 20, 2016 at 02:09

License

BSD

Supported Platforms

noarch