Wednesday, August 7, 2013

Onetime evaluation for Python object method

Following technique described in a previous post, another interesting problem can be solved.

I would like to delay object method evaluation until I access the method and I want to evaluate it only once.

In straight-forward approach, the following can be written:

class BigMath(object):
    def get_big_data(self):
        if hasattr(self, "_rv"):
            return self._rv
        self._rv = self._get_big_data()
        return self._rv
        
    def _get_big_data(self):
        print "working hard...."
        return 42
>>> m = d.BigMath()
>>> m.get_big_data()
working hard....
42
>>> m.get_big_data()
42
>>> 
Hard work is only done once as we can see. Additionally we can rename a get_big_data to big_data and make is property.

The above approach is simple and trivial. However reduplicating it from class to class and from method to method makes things boggy. I would like to have something reusable that I can apply on desired methods. Like this:

class BigMath(object):
    @OneTime
    def big_data(self):
        print "working hard...."
        return 42
Much more elegant, right? Now lets reveal that magic OneTime decorator.
class OneTime(object):
    def __init__(self, func):
        self.func = func

    def __get__(self, obj, cls):
        to_augment = obj or cls
        rv = self.func(to_augment)
        setattr(to_augment, self.func.__name__, rv)
        return rv
Its actually straight-forward if you are familiar with class-as-decorator technique:
  • Again, we create class that behaves both as decorator and descriptor
  • This is parameter-less decorator, so we need to implement only __init__ and not __call__
  • We records the method function
  • When method is accessed (i.e. __get__ gets invoked), we execute the function and cache its results by overriding ourselves.
Enjoy.