Welcome, guest | Sign In | My Account | Store | Cart

Lazy properties can be easily built in Python 2.4 -- properties whose value may require some effort to calculate, but whose values remain constant once calculated. This recipe uses decorators to implements such properties.

Python, 25 lines
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
class Lazy(object):
    def __init__(self, calculate_function):
        self._calculate = calculate_function

    def __get__(self, obj, _=None):
        if obj is None:
            return self
        value = self._calculate(obj)
        setattr(obj, self._calculate.func_name, value)
        return value


# Sample use:

class SomeClass(object):

    @Lazy
    def someprop(self):
        print 'Actually calculating value'
        return 13


o = SomeClass()
o.someprop
o.someprop

In a recent discussion on comp.lang.python, there was discussion about how to provide properties which get calculated once, where you want to avoid the work of doing the calculation at all if you never use the property, but where you get simple access to the property once calculated. This technique evolved from a suggestion by Lief K-Brooks that looked to be something that could be made into an easy-to-use facility with decorators.

4 comments

Alexander Semenov 19 years, 2 months ago  # | flag

Elegant method to invalidate calculated value. Now, after one execution calc-function lost. It's interesting to add some method to invalidate and recalculate property.

Scott David Daniels (author) 19 years, 1 month ago  # | flag

Retracting a value is very simple. Just delete the attribute of the object. Using the sample above:

>>> o = SomeClass()
>>> o.someprop
Actually calculating value
13
>>> o.someprop
13
>>> del o.someprop
>>> o.someprop
Actually calculating value
13
>>> o.someprop
13
Daniel Miller 15 years, 3 months ago  # | flag

Or if you don't feel like implementing the Lazy descriptor:

class SomeClass(object):

    @property
    def someprop(self, v=[]):
        if not v:
            print 'Actually calculating value'
            v[0] = 13
        return v[0]
Daniel Miller 15 years, 3 months ago  # | flag

Argh! Always test before you post (stupid me). The previous version's value was cached on the class, not the instance. Here's a version that works more like the original:

class SomeClass(object):

    @property
    def someprop(self):
        if "someprop" not in self.__dict__:
            print 'Actually calculating value'
            value = 13
            self.__dict__["someprop"] = value
        return self.__dict__["someprop"]

That's starting to get ugly. Oh, and the original solution is better if you need to invalidate the cached value. If you want to invalidate this one you'll need to invoke this incantation:

>>> del s.__dict__["someprop"]
Created by Scott David Daniels on Tue, 18 Jan 2005 (PSF)
Python recipes (4591)
Scott David Daniels's recipes (10)

Required Modules

  • (none specified)

Other Information and Tasks