Chris's Wiki :: blog/python/PropertyLimitation Commentshttps://utcc.utoronto.ca/~cks/space/blog/python/PropertyLimitation?atomcommentsDWiki2008-01-28T01:27:19ZRecent comments in Chris's Wiki :: blog/python/PropertyLimitation.From 75.75.26.88 on /blog/python/PropertyLimitationtag:CSpace:blog/python/PropertyLimitation:08f4d9d0bb1f0daf59d4c60e1df9e2722349073aFrom 75.75.26.88<div class="wikitext"><p>You can't do it with the built-in property, but it's easy to write a non-data descriptor that does it. My version below is not entirely original; Googling on "cached property" or "memoized property" will turn up a number of cookbook recipes and such. However, your spec is stricter than the ones I've seen online, because you require that, once the attribute has been accessed once (and the expensive computation of its value is done), future accesses should be as fast as a normal __dict__ lookup (i.e., no method call allowed).</p>
<p>Here's my code:</p>
<p>((
class CachedProperty(object):</p>
<pre>
"""
Non-data descriptor class for cached property. The
expected typical use case is to be called from the
cached_property function, which generates the name of
the property automatically, but the class can also be
instantiated directly with an explicit name supplied.
"""
def __init__(self, aname, fget, doc=None):
self.aname = aname
self.fget = fget
self.__doc__ = doc
def __get__(self, obj, objtype=None):
if obj is None:
return self
result = self.fget(obj)
setattr(obj, self.aname, result)
return result
</pre>
<p>def cached_property(fget, doc=None):</p>
<pre>
"""
Function to return cached property instance. We need
this as a wrapper to supply the name of the property by
magic rather than force the user to enter it by hand;
this is done by looking up the name of the fget function
(which also allows this function to be used as a decorator
and have the intended effect).
"""
if doc is None:
doc = fget.__doc__
return CachedProperty(fget.__name__, fget, doc)
</pre>
<p>))</p>
<p>This works because a descriptor on a class that doesn't have a __set__ method <em>can</em> be masked by an instance attribute; this is discussed in the Python documentation
<a href="http://docs.python.org/ref/descriptor-invocation.html">here</a>
.</p>
<p>I actually had a use case for this recently, and came across your blog entry in the course of Googling for information; hence the much-delayed comment. I've done some informal timing tests with this compared to a version using the built-in property (so access after the first still involves a method call), and it does make a difference.</p>
<p>Peter Donis</p>
</div>2008-01-28T01:27:19Z