8

Possible Duplicate:
python: are property fields being cached automatically?

As a concern about effeciency of properties in python, I was wondering when and how often they are called.

To use a simple example, say I subclass namedtuple and I have something like:

from collections import namedtuple
from math import pi

class Circle (namedtuple('Circle', 'x, y, r')):
    __slots__ = ()

    @property
    def area(self):
        return pi*self.r**2

unitCircle = Circle(0, 0, 1.0)
print 'The area of the unit circle is {0} units'.format(unitCircle.area)

I assume that area is not calculated until the first time it is called, but once it is called, is that value cached until something changes or is it recalculated every time it is called?

Put another way, if I have a property that (unlike this one) is relatively expensive to calculate and will be used repeatedly, should I let it be a property, or is it more effecient to store it as a value and explicitly caclulate it when it really needs to be updated?

Community
  • 1
  • 1
TimothyAWiseman
  • 14,385
  • 12
  • 40
  • 47
  • You don't have to choose either or. You can have a property that caches itself if not yet loaded, then can be periodically recached on demand. – Silas Ray Sep 06 '12 at 20:08

2 Answers2

18

Properties are not cached unless you do it explicitly, so the code is run every time the property is accessed. Try:

@property
def area(self):
    try:
        return self._area
    except AttributeError:
        area = pi*self.r**2
        self._area = area
        return area

If you want to be able to recalculate the value on demand occasionally, do something like:

@property
def area(self):
    try:
        return self._area
    except AttributeError:
        self.recalc_area()
        return self._area

def recalc_area(self):
    self._area = pi*self.r**2

Or, if you want to do it more automatically:

@property
def area(self):
    try:
        return self._area
    except AttributeError:
        area = pi*self.radius**2
        self._area = area
        return area

@property
def radius(self):
    return self._radius

@radius.setter
def radius(self, radius):
    try:
        del self._area
    except AttributeError:
        pass
    self._radius = radius
Silas Ray
  • 25,682
  • 5
  • 48
  • 63
  • Thanks. So it sounds like a basic property like the example is fine for values that are computationally cheap or referenced rarely. For values that are computationally expensive and referenced repeatedly without changing, something more sophisticated to provide caching seems wise. – TimothyAWiseman Sep 06 '12 at 21:05
  • 1
    Seems like a good analysis. You could even create another decorator that encapsulates `property` that automates the caching process. I've seen examples out there on the web, and even some libraries that make the dependency linking process more explicit for things like area>radius. – Silas Ray Sep 06 '12 at 21:11
1

Decorators in python (like @property) get evaluated when the class is loaded. Sometimes the resulting effects on the class will include extra functions, but the decorator itself is only run once.

desimusxvii
  • 1,094
  • 1
  • 8
  • 10