This decorator runs a function or method once and caches the result.
It offers minimal memory use and high speed (only one extra function call). It is _not_ a memoization implementation, the result is cached for all future arguments as well.
This code is used in the TestOOB testing framework (http://testoob.sourceforge.net).
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
def func_once(func): "A decorator that runs a function only once." def decorated(*args, **kwargs): try: return decorated._once_result except AttributeError: decorated._once_result = func(*args, **kwargs) return decorated._once_result return decorated def method_once(method): "A decorator that runs a method only once." attrname = "_%s_once_result" % id(method) def decorated(self, *args, **kwargs): try: return getattr(self, attrname) except AttributeError: setattr(self, attrname, method(self, *args, **kwargs)) return getattr(self, attrname) return decorated # Example, will only parse the document once @func_once def get_document(): import xml.dom.minidom return xml.dom.minidom.parse("document.xml")
This is a lightweight "run once" decorator. With it you don't have to worry about the inefficiency of recomputing functions that should return the same value in a given run. If they prove to be a performance hit, just add @func_once or @method_once.
This is inspired by Tadayoshi Funaba's "once" implemented in Ruby (see http://www.ruby-doc.org/docs/ProgrammingRuby/html/classes.html, search for "ExampleDate.once").
I tried getting the same benefits as his implementation, mainly having no extra function calls at all, by creating a wrapper class and redefining its __call__ method after the first execution, but I could only get it to work for functions -- not methods. At least there's only one call, no extra calls or conditionals.
Memoization is a more general concept than this recipe, with implementations such as: http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/325905 http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/325205 http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/52201
Update - you can now delete the cached result for functions as follows (only works if the once decorator is the only or last decorator applied):
@func_once def foo(): ...
foo() # computes foo() # cached del foo._once_result foo() # recomputes