Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

lazy properties?

28 views
Skip to first unread message

Andrea Crotti

unread,
Nov 1, 2012, 5:38:53 PM11/1/12
to python-list
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..

Ian Kelly

unread,
Nov 1, 2012, 5:52:17 PM11/1/12
to Python
On Thu, Nov 1, 2012 at 3:38 PM, Andrea Crotti <andrea....@gmail.com> wrote:
> 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..

If you're using Python 3.2+, then functools.lru_cache probably
suffices for your needs.

@property
@functools.lru_cache()
def var_lazy(self):
return long_computation()

If you really need to shorten that to a single declaration:

def lazy_property(func):
return property(functools.lru_cache()(func))

Cameron Simpson

unread,
Nov 1, 2012, 6:08:09 PM11/1/12
to Andrea Crotti, python-list
On 01Nov2012 21:38, Andrea Crotti <andrea....@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 <c...@zip.com.au>

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

Miki Tebeka

unread,
Nov 1, 2012, 7:30:42 PM11/1/12
to Python
> If you're using Python 3.2+, then functools.lru_cache probably
> ...
And if you're on 2.X, you can grab lru_cache from http://code.activestate.com/recipes/498245-lru-and-lfu-cache-decorators/

Miki Tebeka

unread,
Nov 1, 2012, 7:30:42 PM11/1/12
to comp.lan...@googlegroups.com, Python
> If you're using Python 3.2+, then functools.lru_cache probably

Stefan H. Holek

unread,
Nov 2, 2012, 4:36:11 AM11/2/12
to Andrea Crotti, python-list
On 01.11.2012, at 22:38, Andrea Crotti 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?

There is a ready made and well tested lazy decorator at http://pypi.python.org/pypi/lazy

Stefan

--
Stefan H. Holek
ste...@epy.co.at

0 new messages