[Tutor] Properties - when and why [using caching]

Danny Yoo dyoo at hkn.eecs.berkeley.edu
Mon Jul 8 03:55:55 EDT 2002


On Sun, 7 Jul 2002, Arthur Siegel wrote:

> Say self.W and self.U are each 3 element vectors referenced on
> initialization, with attributes x,y, z. V is a calculated vector.  V=W-U
>
> def get_V(self):
>    return self.W-self.U
>
> V=properties(get_V)
>
> But occasionally I need to access V's attributes individually.
>
> def V_homogenous(self):
>     return array((V.x,V.y,V.z,1.))
>
> Have I not just made three calls to the vector subtraction
> of the get_V method?

Hi Arthur,

Yes, but this actually isn't as bad as one might think.  The "refactoring"
folks, in fact, recommend something like this, turning attribute access
into a method call.  I think properties are there to disguise what looks
like a simple attribute access with a full-fledged method that we can
control.


You're right that separate access to V.x, V.y, and V.z will cause Python
to call get_V three times, and each time will compute that vector
subtraction.  But we can fix this!  To reduce the cost of computing
methods, we can use a cache that saves the last calculation of
'self.W-self.U'.  That is, compute it once, and then cache it aside.

###
def __init__(self):
    ## Somewhere within the initializer, let's make our class as being
    ## "dirty"
    self._dirty_V = 1

def get_V(self):
    if self._dirty_V:
        self._precomputed_V = self.W - self.U
        self._dirty_V = 0
    return self._precomputed_V
###


As long as the 'x', 'y', and 'z' attributes don't change, then there's no
need to do that expensive vector subtraction again whenever we want 'V'.
We should also attach, within each of the set()er property methods, a
little switch to make 'V' dirty again.


Notice that because get_V() is a method, we can do this optimization
without disturbing the user of our class instance.  This is a great
demonstration of the advantage of properties.  Without the use of
properties, we can't smuggle this caching code within an attribute access,
at least, not without writing really tricky code...  *grin*



> Could have done:
> def get_V(self):
>    self._V  = self.W - self.V
>    return self._V
>
> def V_homogenous(self):
>     return array((self._V.x,self._V.y,self._V.z,1.))


Yes, this is the same idea.  We do need to make sure that get_V() gets
called before V_homogenous():  otherwise, self._V may not be initialized
by then if we're not careful.


Hope this helps!






More information about the Python-list mailing list