lazy properties?

Cameron Simpson cs at zip.com.au
Thu Nov 1 23:08:09 CET 2012


On 01Nov2012 21:38, Andrea Crotti <andrea.crotti.0 at gmail.com> wrote:
| Seeing the wonderful "lazy val" in Scala I thought that I should try to 
| get the following also in Python.
| The problem is that I often have this pattern in my code:
| 
| class Sample:
|      def __init__(self):
|          self._var = None
| 
|      @property
|      def var(self):
|          if self._var is None:
|              self._var = long_computation()
|          else:
|              return self._var
| 
| 
| which is quite useful when you have some expensive attribute to compute 
| that is not going to change.
| I was trying to generalize it in a @lazy_property but my attempts so far 
| failed, any help on how I could do that?
| 
| What I would like to write is
|      @lazy_property
|      def var_lazy(self):
|          return long_computation()
| 
| and this should imply that the long_computation is called only once..

I've got one of these which I use exactly as you wish above:

  def lazy_property(func):
    ''' A property whose access is controlled by a lock if unset.
    '''
    lock_name = '_lock'
    prop_name = '_' + func.__name__
    unset_object = None
    def getprop(self):
      ''' Attempt lockless fetch of property first.
          Use lock if property is unset.
      '''
      p = getattr(self, prop_name)
      if p is unset_object:
        with getattr(self, lock_name):
          p = getattr(self, prop_name)
          if p is unset_object:
            ##debug("compute %s...", prop_name)
            p = func(self)
            ##warning("compute %s[%s].%s: %s", self, id(self), prop_name,
  type(p))
            setattr(self, prop_name, p)
      return p
    return property(getprop)

It computes the cached property name from the function name, but uses a
global lock name "_lock" on the basis that the long_computation() will
use shared state with the rest of the object.

The microoptimisation of the lockless fetch may be either nonportable or
pointless.

I need to abstract this with a deeper level of nesting to support
chaning lock_name, prop_name and unset_object if the caller desires, but
for what you want it will work out of the box.

I've got a similar thing that watches files for modification and reloads
at need.

Cheers,
-- 
Cameron Simpson <cs at zip.com.au>

Cordless hoses have been around for quite some time. They're called buckets.
        - Dan Prener <prener at watson.ibm.com>


More information about the Python-list mailing list