import functools


def LazyProperty(func):
  """A decorator for lazily calculated properties.

  This decorator implicitly creates a read-only property with added logic
  to cache the result.  It can be used in conjuction with the LazyBase
  base class, or completely on its own.

  Typical use:

  class Foo(obect):

    @lazy.LazyProperty
    def bar(self):
      ...

  f = Foo()
  f.bar  # will return result of f.bar()
  f.bar  # will return previous result without calling bar() again

  Args:
    func: A function that computes the value of the property.

  Returns:
    A property object.
  """

  def _LazyFunc(self):
    name = func.__name__
    try:
      return self._LazyBase__lazy_values[name]  # pylint: disable-msg=W0212
    except AttributeError:
      # auto-populate this attribute so that no __init__ code is required
      self._LazyBase__lazy_values = {}
    except KeyError:
      # property hasn't been evaluated yet, fall through
      pass
    # call the underlying function to compute the value
    value = func(self)
    self._LazyBase__lazy_values[name] = value  # pylint: disable-msg=W0212
    return value

  functools.update_wrapper(_LazyFunc, func)
  return property(_LazyFunc)