Mailing List Archive

Newbie again: computing attributes on the fly
Hi everybody,

here's another couple of questions to the enlightened ones. I'd like to
compute certain properties of a class instance only once and store it for
future use. I'd like to do those computations on demand only, because some
might be really, really expensive. My first try looks like this:

class x:
def __getattr__(self, attr):
import string
if string.find(attr, "get_") != 0:
val = ( lambda x = getattr(self, "get_"+attr)(): x )
self.__dict__[attr] = val
return val
else:
raise AttributeError("no such attribute")

def get_lost(self):
return 0 # really, really expensive :)

def get_a_life(self):
return 1

>>> y = x()
>>> y.lost()
0
>>> y.a_life()
1

Here, the lambda is pointless somehow, but hints to a potential desire to
add parameter support in the future (which I think I don't need, but who
knows?).

Question(s):
1) Is this 'good', or at least 'okay' style?
2) Any immediate suggestions for improvement?
3) Has anyone ever bothered to implement a more elegant or more complete
approach, which might, for example, include such things as parameters,
adding memorization to existing classes, you name it?

Thanks for reading,
Olaf

--
//// Olaf Delgado Friedrichs, Uni Bielefeld, Mathematik
Olaf Postfach 100131 (room: V4-109)
`=' D-33501 Bielefeld, Germany (phone: +49 521 106-4765)
http://www.mathematik.uni-bielefeld.de/~delgado
Newbie again: computing attributes on the fly [ In reply to ]
Olaf Delgado (delgado@Mathematik.Uni-Bielefeld.DE) wrote:
: Hi everybody,
:
: here's another couple of questions to the enlightened ones. I'd like to
: compute certain properties of a class instance only once and store it for
: future use. I'd like to do those computations on demand only, because some
: might be really, really expensive. My first try looks like this:
:
: class x:
: def __getattr__(self, attr):
: import string
: if string.find(attr, "get_") != 0:
: val = ( lambda x = getattr(self, "get_"+attr)(): x )
: self.__dict__[attr] = val
: return val
: else:
: raise AttributeError("no such attribute")
:
: def get_lost(self):
: return 0 # really, really expensive :)
:
: def get_a_life(self):
: return 1

On occasion I've done things like this (not necessarily for cost reasons,
but I just might want to do something different the first time). What
I've done is:

class x:
def dosomething(self, arg):
initial blah, blah, blah
self.dosomething = self._dosomething2
def _dosomething2(self, arg):
second_and_subsequent blah, blah, blah

--
-=-=-=-=-=-=-=-=
Clarence Gardner
AvTel Communications
Software Products and Services Division
clarence@avtel.com
Newbie again: computing attributes on the fly [ In reply to ]
Olaf Delgado writes:
>
> here's another couple of questions to the enlightened ones. I'd like
> to compute certain properties of a class instance only once and
> store it for future use. I'd like to do those computations on demand
> only, because some might be really, really expensive. My first try
> looks like this:
>
> class x:
> def __getattr__(self, attr):
> import string
> if string.find(attr, "get_") != 0:
> val = ( lambda x = getattr(self, "get_"+attr)(): x )
> self.__dict__[attr] = val return val
> else:
> raise AttributeError("no such attribute")
>
> def get_lost(self):
> return 0 # really, really expensive :)
>
> def get_a_life(self):
> return 1
>
> >>> y = x()
> >>> y.lost()
> 0
> >>> y.a_life()
> 1
>
> Here, the lambda is pointless somehow, but hints to a potential
> desire to add parameter support in the future (which I think I don't
> need, but who knows?).
>
> Question(s):
> 1) Is this 'good', or at least 'okay' style?
> 2) Any immediate suggestions for improvement?
> 3) Has anyone ever bothered to implement a more elegant or more
> complete
> approach, which might, for example, include such things as
> parameters, adding memorization to existing classes, you name it?

You're making things too complicated. __getattr__ is only called if
the attribute is not found. So something like this will suffice:

>>> class X:
... def __getattr__(self, nm):
... if nm == 'foo':
... self.foo = 17.0 * 33.1 + .01
... return self.foo
... raise AttributeError("no attribute %s" % nm)
...
>>> x = X()
>>> dir(x)
[] #no instance attributes
>>> x.foo
562.71
>>> dir(x)
['foo'] #now foo is an instance attribute

If you want to generalize that, I'd see if we have an attribute
"calc_%s" % nm and use that method (vs the inline computation).

- Gordon
Newbie again: computing attributes on the fly [ In reply to ]
Hi and thanks for answering!

On Wed, 16 Jun 1999, Gordon McMillan wrote:

> Olaf Delgado writes:
> >
> > class x:
> > def __getattr__(self, attr):
> > import string
> > if string.find(attr, "get_") != 0:
> > val = ( lambda x = getattr(self, "get_"+attr)(): x )
> > self.__dict__[attr] = val
> > return val
> > else:
> > raise AttributeError("no such attribute")
> > [...]


> You're making things too complicated. __getattr__ is only called if
> the attribute is not found. So something like this will suffice:
> [...]
> If you want to generalize that, I'd see if we have an attribute
> "calc_%s" % nm and use that method (vs the inline computation).

Yes, I definitely want to generalize that. The problem is that that method
may be defined in a superclass, so "self.__dict__" won't find it. AFAIK
(which may be not be very far), I have to use "hasattr" and/or "getattr",
which in turn will call "__getattr__".

Recursively yours,
Olaf

--
//// Olaf Delgado Friedrichs, Uni Bielefeld, Mathematik
Olaf Postfach 100131 (room: V4-109)
`=' D-33501 Bielefeld, Germany (phone: +49 521 106-4765)
http://www.mathematik.uni-bielefeld.de/~delgado
Newbie again: computing attributes on the fly [ In reply to ]
On Wed, 16 Jun 1999, Olaf Delgado wrote:

> Yes, I definitely want to generalize that. The problem is that that method
> may be defined in a superclass, so "self.__dict__" won't find it. AFAIK
> (which may be not be very far), I have to use "hasattr" and/or "getattr",
> which in turn will call "__getattr__".

You may find that using ExtensionClass could help. See:

http://www.digicool.com/releases/ExtensionClass/

(note especially the section on computed attributes in the documentation).

--david
Newbie again: computing attributes on the fly [ In reply to ]
[Olaf on my very simple getattr hack]:

> Yes, I definitely want to generalize that. The problem is that that
> method may be defined in a superclass, so "self.__dict__" won't find
> it. AFAIK (which may be not be very far), I have to use "hasattr"
> and/or "getattr", which in turn will call "__getattr__".

Oh, getattr isn't bad. Wait 'till you try setattr! Anyway, this
should demonstrate how to control the recursion:

class X:
def __getattr__(self, nm):
print "Looking for", nm
mthdnm = "calc_%s" %nm
print "Looking in instance..."
mthd = self.__dict__.get(mthdnm, None)
if mthd is None:
print "Looking in class hierarchy..."
try:
mthd = getattr(self.__class__, mthdnm)
except AttributeError:
pass
if mthd:
val = mthd(self)
self.__dict__[nm] = val
return val
raise AttributeError("no attribute %s" % nm)

x = X()
try:
print x.foo
except AttributeError:
print "foo not found in x"

class Y(X):
def calc_foo(self):
return 33.0 * 17

y = Y()
print y.foo
print "second time..."
print y.foo

Which should yield:

Looking for foo
Looking in instance...
Looking in class hierarchy...
foo not found in x
Looking for foo
Looking in instance...
Looking in class hierarchy...
561.0
second time...
561.0

- Gordon
Newbie again: computing attributes on the fly [ In reply to ]
On Wed, 16 Jun 1999, David Ascher wrote:

> You may find that using ExtensionClass could help. See:
>
> http://www.digicool.com/releases/ExtensionClass/
>
> (note especially the section on computed attributes in the documentation).

Oops! That's the kind of advice I was fearing. Last time I tried to
understand that stuff, I really got dizzy.

Anyway, thanks! I'll have a look at it once more.

Yours,
Olaf

--
//// Olaf Delgado Friedrichs, Uni Bielefeld, Mathematik
Olaf Postfach 100131 (room: V4-109)
`=' D-33501 Bielefeld, Germany (phone: +49 521 106-4765)
http://www.mathematik.uni-bielefeld.de/~delgado
Newbie again: computing attributes on the fly [ In reply to ]
On Wed, 16 Jun 1999, Gordon McMillan wrote:

> [Olaf on my very simple getattr hack]:
>
> > Yes, I definitely want to generalize that. The problem is that that
> > method may be defined in a superclass, so "self.__dict__" won't find
> > it. AFAIK (which may be not be very far), I have to use "hasattr"
> > and/or "getattr", which in turn will call "__getattr__".
>
> Oh, getattr isn't bad. Wait 'till you try setattr! Anyway, this
> should demonstrate how to control the recursion:
[...]

Yes, that looks much more civilized than my version. So, I'll have to take
a closer look at all these magic methods.

--
//// Olaf Delgado Friedrichs, Uni Bielefeld, Mathematik
Olaf Postfach 100131 (room: V4-109)
`=' D-33501 Bielefeld, Germany (phone: +49 521 106-4765)
http://www.mathematik.uni-bielefeld.de/~delgado