Velocity Reviews - Computer Hardware Reviews

Velocity Reviews > Newsgroups > Programming > Python > One-Shot Property?

Reply
Thread Tools

One-Shot Property?

 
 
Kevin Smith
Guest
Posts: n/a
 
      01-18-2005

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
http://www.velocityreviews.com/forums/(E-Mail Removed)
 
Reply With Quote
 
 
 
 
Dave Benjamin
Guest
Posts: n/a
 
      01-18-2005
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
 
Reply With Quote
 
 
 
 
Daniel Dittmar
Guest
Posts: n/a
 
      01-18-2005
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
 
Reply With Quote
 
John Lenton
Guest
Posts: n/a
 
      01-18-2005
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 ((E-Mail Removed)) -- 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( man page

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

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

 
Reply With Quote
 
Leif K-Brooks
Guest
Posts: n/a
 
      01-18-2005
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}
 
Reply With Quote
 
Scott David Daniels
Guest
Posts: n/a
 
      01-18-2005
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
(E-Mail Removed)
 
Reply With Quote
 
Terry Hancock
Guest
Posts: n/a
 
      01-28-2005
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

 
Reply With Quote
 
 
 
Reply

Thread Tools

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off
Trackbacks are On
Pingbacks are On
Refbacks are Off




Advertisments