By using this site, you agree to our updated Privacy Policy and our Terms of Use. Manage your Cookies Settings.
440,172 Members | 762 Online
Bytes IT Community
+ Ask a Question
Need help? Post your question and get tips & solutions from a community of 440,172 IT Pros & Developers. It's quick & easy.

One-Shot Property?

P: n/a

I have many cases in my code where I use a property for calculating a
value on-demand. Quite a few of these only need to be called once.
After that the value is always the same. In these properties, I set a
variable in the instance as a cached value and return that value on
subsequent calls. It would be nice if there was a descriptor that would
do this automatically. Actually, what would be really nice is if I
could replace the property altogether and put the calculated value in
its place after the first call, but the property itself prevents me from
doing that. Is this possible?

--
Kevin Smith
Ke*********@sas.com
Jul 18 '05 #1
Share this Question
Share on Google+
6 Replies


P: n/a
Kevin Smith wrote:
I have many cases in my code where I use a property for calculating a
value on-demand. Quite a few of these only need to be called once.
After that the value is always the same. In these properties, I set a
variable in the instance as a cached value and return that value on
subsequent calls. It would be nice if there was a descriptor that would
do this automatically. Actually, what would be really nice is if I
could replace the property altogether and put the calculated value in
its place after the first call, but the property itself prevents me from
doing that. Is this possible?


I was going to recommend taking a look at the "memoize" example on the
Python wiki, but it seems to be missing at the moment. In any case,
here's the URL:

http://www.python.org/moin/PythonDecoratorLibrary

There are also a few examples on the Cookbook, like this one:

http://aspn.activestate.com/ASPN/Coo.../Recipe/325205

It shouldn't be too difficult to adapt this technique so that it can be
used to create properties. I wouldn't bother replacing the property with
an attribute unless you have a specific reason to do so (performance,
perhaps).

HTH,
Dave
Jul 18 '05 #2

P: n/a
Kevin Smith wrote:
I have many cases in my code where I use a property for calculating a
value on-demand. Quite a few of these only need to be called once.
After that the value is always the same. In these properties, I set a
variable in the instance as a cached value and return that value on
subsequent calls. It would be nice if there was a descriptor that would
do this automatically. Actually, what would be really nice is if I
could replace the property altogether and put the calculated value in
its place after the first call, but the property itself prevents me from
doing that. Is this possible?


If you use the old-fashioned __getattr__ method instead of properties.
__getattr__ gets called only if the value can't be found in the instance
dictionary.

def __getattr__ (self, attrname):
try:
method = getattr (self, 'calculate_' + attrname)
except AttributeError:
raise AttributeError, attrname
value = method ()
setattr (self, attrname, value)
return value

And probably also through metaclasses. And decorators.

Daniel
Jul 18 '05 #3

P: n/a
Kevin Smith wrote:
I have many cases in my code where I use a property for calculating a
value on-demand. Quite a few of these only need to be called once.
After that the value is always the same. In these properties, I set a
variable in the instance as a cached value and return that value on
subsequent calls. It would be nice if there was a descriptor that would
do this automatically. Actually, what would be really nice is if I
could replace the property altogether and put the calculated value in
its place after the first call, but the property itself prevents me from
doing that.


This should do it:

class CachingProperty(object):
def __init__(self, attr_name, calculate_function):
self._name = attr_name
self._calculate = calculate_function

def __get__(self, obj, type=None):
if obj is None:
return self
else:
value = self._calculate(obj)
setattr(obj, self._name, value)
return value

And example code:
class Foo(object): .... def calculate_value(self):
.... print 'Calculating...'
.... return 42
.... foo = CachingProperty('foo', calculate_value)
.... bar = Foo()
bar.__dict__ {} bar.foo Calculating...
42 bar.foo # Notice that the print statement doesn't run this time 42 bar.__dict__

{'foo': 42}
Jul 18 '05 #4

P: n/a
On Tue, Jan 18, 2005 at 04:54:56PM +0000, Kevin Smith wrote:

I have many cases in my code where I use a property for calculating a
value on-demand. Quite a few of these only need to be called once.
After that the value is always the same. In these properties, I set a
variable in the instance as a cached value and return that value on
subsequent calls. It would be nice if there was a descriptor that would
do this automatically. Actually, what would be really nice is if I
could replace the property altogether and put the calculated value in
its place after the first call, but the property itself prevents me from
doing that. Is this possible?


consider this:

1 >>> class C:
2 ... x = property(str)
3 ...
4 >>> c = C()
5 >>> c.x
6 '<__main__.C instance at 0x4008d92c>'
7 >>> setattr(c, 'x', c.x)
8 >>> c.x
9 '<__main__.C instance at 0x4008d92c>'
10 >>> C.x
11 <property object at 0x4009a7ac>
12 >>> c.x = 2
13 >>>

in line 5 you see that the x property so defined works. In line 7 you
remove it, replacing it with the computed value of the property. Line
9 shows that it worked, line 11 shows that it didn't break the class,
and line 13 (through the absence of an exception) shows that it no
longer is 'special' (as it shouldn't be).

--
John Lenton (jo**@grulic.org.ar) -- Random fortune:
You can tune a piano, but you can't tuna fish.

You can tune a filesystem, but you can't tuna fish.
-- from the tunefs(8) man page

-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.2.5 (GNU/Linux)

iD8DBQFB7VZmgPqu395ykGsRApG8AKCWsaaRwbKN8gWCQw2E1g XryWCpZACgrbHz
CjrrwkO6QkZ1ziMG6GKXQug=
=+cST
-----END PGP SIGNATURE-----

Jul 18 '05 #5

P: n/a
Leif K-Brooks wrote:

class CachingProperty(object):
def __init__(self, attr_name, calculate_function):
self._name = attr_name
self._calculate = calculate_function
def __get__(self, obj, type=None):
if obj is None:
return self
else:
value = self._calculate(obj)
setattr(obj, self._name, value)
return value

And example code:
>>> class Foo(object): ... def calculate_value(self):
... print 'Calculating...'
... return 42
... foo = CachingProperty('foo', calculate_value)
... >>> bar = Foo()
>>> bar.__dict__ {} >>> bar.foo Calculating...
42 >>> bar.foo # Notice that the print statement doesn't run this time 42 >>> bar.__dict__

{'foo': 42}

To build on this for Python 2.4:

class Caches(object):
def __init__(self, calculate_function):
self._calculate = calculate_function

def __get__(self, obj, _=None):
if obj is None:
return self
value = self._calculate(obj)
setattr(obj, self._calculate.func_name, value)
return value
class Foo(object):
@Caches
def foo(self):
print 'Calculating...'
return 42

--Scott David Daniels
Sc***********@Acm.Org
Jul 18 '05 #6

P: n/a
On Tuesday 18 January 2005 12:33 pm, John Lenton wrote:

consider this:

1 >>> class C:
2 ... x = property(str)
3 ...
4 >>> c = C()
5 >>> c.x
6 '<__main__.C instance at 0x4008d92c>'
7 >>> setattr(c, 'x', c.x)
8 >>> c.x
9 '<__main__.C instance at 0x4008d92c>'
10 >>> C.x
11 <property object at 0x4009a7ac>
12 >>> c.x = 2
13 >>>

in line 5 you see that the x property so defined works. In line 7 you
remove it, replacing it with the computed value of the property. Line
9 shows that it worked, line 11 shows that it didn't break the class,
and line 13 (through the absence of an exception) shows that it no
longer is 'special' (as it shouldn't be).
It wasn't "special" before, either -- I tried this myself, because I wasn't
familiar with properties yet, and I was trying to figure out what you
meant by that.

The catch is that I think you meant to type:
1 >>> class C(object):


(i.e. so that C is a "new-style" class).

Then we get "special" behavior (which answers my first question):
class C(object): .... x = property(str)
.... C.x <property object at 0x401e8cd4> c = C()
c.x '<__main__.C object at 0x401e984c>' c.x = 2 Traceback (most recent call last):
File "<stdin>", line 1, in ?
AttributeError: can't set attribute
Unfortunately this seems to break your technique, too:
setattr(c, 'x', c.x)

Traceback (most recent call last):
File "<stdin>", line 1, in ?
AttributeError: can't set attribute

Too bad. I was kind of hoping it was just a typo. :-(

Unless I'm missing something, anyway.

Cheers,
Terry

--
--
Terry Hancock ( hancock at anansispaceworks.com )
Anansi Spaceworks http://www.anansispaceworks.com

Jul 18 '05 #7

This discussion thread is closed

Replies have been disabled for this discussion.