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 42Much 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 rvIts 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.
No comments:
Post a Comment