115

Recently I've gone through an existing code base containing many classes where instance attributes reflect values stored in a database. I've refactored a lot of these attributes to have their database lookups be deferred, ie. not be initialised in the constructor but only upon first read. These attributes do not change over the lifetime of the instance, but they're a real bottleneck to calculate that first time and only really accessed for special cases. Hence they can also be cached after they've been retrieved from the database (this therefore fits the definition of memoisation where the input is simply "no input").

I find myself typing the following snippet of code over and over again for various attributes across various classes:

class testA(object):

  def __init__(self):
    self._a = None
    self._b = None

  @property
  def a(self):
    if self._a is None:
      # Calculate the attribute now
      self._a = 7
    return self._a

  @property
  def b(self):
    #etc

Is there an existing decorator to do this already in Python that I'm simply unaware of? Or, is there a reasonably simple way to define a decorator that does this?

I'm working under Python 2.5, but 2.6 answers might still be interesting if they are significantly different.

Note

This question was asked before Python included a lot of ready-made decorators for this. I have updated it only to correct terminology.

4
  • I'm using Python 2.7, and I don't see anything about ready-made decorators for this. Can you provide a link to the ready-made decorators that are mentioned in the question?
    – Bamcclur
    Commented Aug 26, 2016 at 22:17
  • @Bamcclur sorry, there used to be other comments detailing them, not sure why they got deleted. The only one I can find right now is a Python 3 one: functools.lru_cache().
    – detly
    Commented Aug 27, 2016 at 2:45
  • Not sure there are built-ins (at least Python 2.7), but there's the Boltons library's cachedproperty
    – Guy
    Commented Oct 25, 2016 at 19:45
  • @guyarad I didn't see this comment until now. That is a fantastic library! Post that as an answer so I can upvote it.
    – detly
    Commented Jun 27, 2017 at 22:23

10 Answers 10

129

Here is an example implementation of a lazy property decorator:

import functools

def lazyprop(fn):
    attr_name = '_lazy_' + fn.__name__

    @property
    @functools.wraps(fn)
    def _lazyprop(self):
        if not hasattr(self, attr_name):
            setattr(self, attr_name, fn(self))
        return getattr(self, attr_name)

    return _lazyprop


class Test(object):

    @lazyprop
    def a(self):
        print 'generating "a"'
        return range(5)

Interactive session:

>>> t = Test()
>>> t.__dict__
{}
>>> t.a
generating "a"
[0, 1, 2, 3, 4]
>>> t.__dict__
{'_lazy_a': [0, 1, 2, 3, 4]}
>>> t.a
[0, 1, 2, 3, 4]
11
  • 1
    Can someone recommend an appropriate name for the inner function? I'm so bad at naming things in the morning...
    – Mike Boers
    Commented Jun 10, 2010 at 11:29
  • 2
    I usually name the inner function the same as the outer function with a preceding underscore. So "_lazyprop" - follows the "internal use only" philosophy of pep 8.
    – spenthil
    Commented Jun 10, 2010 at 16:29
  • 1
    This works great :) I don't know why it never occurred to me to use a decorator on a nested function like that, too.
    – detly
    Commented Jun 11, 2010 at 2:09
  • 4
    given the non-data descriptor protocol, this one is much slower and less elegant than the answer below using __get__
    – user78110
    Commented Dec 18, 2012 at 18:40
  • 1
    Tip: Put a @wraps(fn) below @property to not loose your doc strings etc. (wraps comes from functools)
    – letmaik
    Commented May 29, 2014 at 10:12
115

I wrote this one for myself... To be used for true one-time calculated lazy properties. I like it because it avoids sticking extra attributes on objects, and once activated does not waste time checking for attribute presence, etc.:

import functools

class lazy_property(object):
    '''
    meant to be used for lazy evaluation of an object attribute.
    property should represent non-mutable data, as it replaces itself.
    '''

    def __init__(self, fget):
        self.fget = fget

        # copy the getter function's docstring and other attributes
        functools.update_wrapper(self, fget)

    def __get__(self, obj, cls):
        if obj is None:
            return self

        value = self.fget(obj)
        setattr(obj, self.fget.__name__, value)
        return value


class Test(object):

    @lazy_property
    def results(self):
        calcs = 1  # Do a lot of calculation here
        return calcs

Note: The lazy_property class is a non-data descriptor, which means it is read-only. Adding a __set__ method would prevent it from working correctly.

7
  • 10
    This took a little while to understand but is an absolutely stunning answer. I like how the function itself is replaced by the value it calculates. Commented Sep 17, 2013 at 18:07
  • 3
    For posterity: other versions of this have been proposed in other answers since (ref 1and 2). Seems this is a popular one in Python web frameworks (derivatives exist in Pyramid and Werkzeug). Commented Dec 5, 2013 at 11:20
  • 1
    Thanks for noting that Werkzeug has werkzeug.utils.cached_property: werkzeug.pocoo.org/docs/utils/#werkzeug.utils.cached_property
    – divieira
    Commented Jan 4, 2014 at 1:15
  • 3
    I found this method to be 7.6 times faster than the selected answer. (2.45 µs / 322 ns) See ipython notebook Commented Feb 28, 2014 at 19:49
  • 1
    NB: this does not prevent assignment to fget the way @property does. To ensure immutability/idempotence, you need to add a __set__() method that raises AttributeError('can\'t set attribute') (or whatever exception/message suits you, but this is what property raises). This unfortunately comes with a performance impact of a fraction of a microsecond because __get__() will be called on each access rather than pulling fget value from dict on second and subsequent access. Well worth it in my opinion to maintain immutability/idempotence, which is key for my use cases, but YMMV.
    – scanny
    Commented Sep 11, 2018 at 23:55
16

For all sorts of great utilities I'm using boltons.

As part of that library you have cachedproperty:

from boltons.cacheutils import cachedproperty

class Foo(object):
    def __init__(self):
        self.value = 4

    @cachedproperty
    def cached_prop(self):
        self.value += 1
        return self.value


f = Foo()
print(f.value)  # initial value
print(f.cached_prop)  # cached property is calculated
f.value = 1
print(f.cached_prop)  # same value for the cached property - it isn't calculated again
print(f.value)  # the backing value is different (it's essentially unrelated value)
5

property is a class. A descriptor to be exact. Simply derive from it and implement the desired behavior.

class lazyproperty(property):
   ....

class testA(object):
   ....
  a = lazyproperty('_a')
  b = lazyproperty('_b')
4

Here's a callable that takes an optional timeout argument, in the __call__ you could also copy over the __name__, __doc__, __module__ from func's namespace:

import time

class Lazyproperty(object):

    def __init__(self, timeout=None):
        self.timeout = timeout
        self._cache = {}

    def __call__(self, func):
        self.func = func
        return self

    def __get__(self, obj, objcls):
        if obj not in self._cache or \
          (self.timeout and time.time() - self._cache[key][1] > self.timeout):
            self._cache[obj] = (self.func(obj), time.time())
        return self._cache[obj]

ex:

class Foo(object):

    @Lazyproperty(10)
    def bar(self):
        print('calculating')
        return 'bar'

>>> x = Foo()
>>> print(x.bar)
calculating
bar
>>> print(x.bar)
bar
...(waiting 10 seconds)...
>>> print(x.bar)
calculating
bar
4

They added exactly what you're looking for in python 3.8

Transform a method of a class into a property whose value is computed once and then cached as a normal attribute for the life of the instance. Similar to property(), with the addition of caching.

Use it just like @property :

@cached_property
def a(self):
    self._a = 7
    return self._a
3

What you really want is the reify (source linked!) decorator from Pyramid:

Use as a class method decorator. It operates almost exactly like the Python @property decorator, but it puts the result of the method it decorates into the instance dict after the first call, effectively replacing the function it decorates with an instance variable. It is, in Python parlance, a non-data descriptor. The following is an example and its usage:

>>> from pyramid.decorator import reify

>>> class Foo(object):
...     @reify
...     def jammy(self):
...         print('jammy called')
...         return 1

>>> f = Foo()
>>> v = f.jammy
jammy called
>>> print(v)
1
>>> f.jammy
1
>>> # jammy func not called the second time; it replaced itself with 1
>>> # Note: reassignment is possible
>>> f.jammy = 2
>>> f.jammy
2
4
  • 1
    Nice one, does exactly what I needed... although Pyramid might be a big dependency for one decorator :)
    – detly
    Commented Dec 4, 2016 at 18:14
  • @detly The decorator implementation is simple, and you could implement it yourself, no need for the pyramid dependency. Commented Sep 24, 2018 at 15:17
  • Hence the link says "source linked" :D Commented Sep 24, 2018 at 15:36
  • @AnttiHaapala I noticed, but thought I'd highlight that it's simple to implement for those that don't follow the link. Commented Sep 25, 2018 at 8:29
2

There is a mix up of terms and/or confusion of concepts both in question and in answers so far.

Lazy evaluation just means that something is evaluated at runtime at the last possible moment when a value is needed. The standard @property decorator does just that.(*) The decorated function is evaluated only and every time you need the value of that property. (see wikipedia article about lazy evaluation)

(*)Actually a true lazy evaluation (compare e.g. haskell) is very hard to achieve in python (and results in code which is far from idiomatic).

Memoization is the correct term for what the asker seems to be looking for. Pure functions that do not depend on side effects for return value evaluation can be safely memoized and there is actually a decorator in functools @functools.lru_cache so no need for writing own decorators unless you need specialized behavior.

8
  • I used the term "lazy" because in the original implementation, the member was computed/retrieved from a DB at the time of object initialisation, and I want to defer that computation until the property was actually used in a template. This seemed to me to match the definition of laziness. I agree that since my question already assumes a solution using @property, "lazy" doesn't make a lot of sense at that point. (I also thought of memoisation as a map of inputs to cached outputs, and since these properties have only one input, nothing, a map seemed like more complexity than necessary.)
    – detly
    Commented Feb 24, 2016 at 20:21
  • Note that all of the decorators that people have suggested as "out of the box" solutions didn't exist when I asked this, either.
    – detly
    Commented Feb 24, 2016 at 20:21
  • I agree with Jason, this is a question about caching/memoization not lazy evaluation.
    – poindexter
    Commented Mar 1, 2016 at 0:55
  • @poindexter - Caching doesn't quite cover it; it does not distinguish looking the value up at object init time and caching it from looking the value up and caching it when the property is accessed (which is the key feature here). What should I call it? "Cache-after-first-use" decorator?
    – detly
    Commented Mar 18, 2016 at 3:43
  • @detly Memoize. You should call it Memoize. en.wikipedia.org/wiki/Memoization
    – poindexter
    Commented Mar 18, 2016 at 13:55
1

You can do this nice and easily by building a class from Python native property:

class cached_property(property):
    def __init__(self, func, name=None, doc=None):
        self.__name__ = name or func.__name__
        self.__module__ = func.__module__
        self.__doc__ = doc or func.__doc__
        self.func = func

    def __set__(self, obj, value):
        obj.__dict__[self.__name__] = value

    def __get__(self, obj, type=None):
        if obj is None:
            return self
        value = obj.__dict__.get(self.__name__, None)
        if value is None:
            value = self.func(obj)
            obj.__dict__[self.__name__] = value
        return value

We can use this property class like regular class property ( It's also support item assignment as you can see)

class SampleClass():
    @cached_property
    def cached_property(self):
        print('I am calculating value')
        return 'My calculated value'


c = SampleClass()
print(c.cached_property)
print(c.cached_property)
c.cached_property = 2
print(c.cached_property)
print(c.cached_property)

Value only calculated first time and after that we used our saved value

Output:

I am calculating value
My calculated value
My calculated value
2
2
0

I agree with @jason When I think about lazy evaluation, Asyncio immediately comes to mind. The possibility of delaying the expensive calculation till the last minute is the sole benefit of lazy evaluation.

Caching / memozition on the other hand could be beneficial but on the expense that the calculation is static and won't change with time / inputs.

A practice I often do for expensive calculations of these sorts is to calculate then cache with TTL.

Not the answer you're looking for? Browse other questions tagged or ask your own question.