def get_important_data(self): if not hasattr(self, _important_data): self._important_data = self.get_result_of_lots_of_queries() return self._important_dataIf you call this function once on an object, it will run a lot of queries, but will store the result in self._important_data. The next time you call the function on this exact same object, the attribute self._important_data will still be set and the huge load of queries do not have to be executed again.
In some projects you have to create such a function more than once, for whatever reason. A colleague got sick and tired of writing the same code over and over again and came up with an idea to tackle this concurrency. So he asked me if, when I had little to do, I could write a decorator that does exactly the same kind of 'caching' as the function above. I however have little to no experience in writing decorators, so I copy/pasted something together from the interwebz, that seems to do what I want.
This is what I came up with.
from functools import wraps def cached_function(attr): def inner_cached_function(fn): def return_attr(*args, **kwargs): try: cls = args.__class__ # args should be fn's self except: raise Exception("""A function with the cached_function decorator must use 'self' as first argument""") if not hasattr(cls, attr): value = fn(*args, **kwargs) setattr(cls, attr, value) return getattr(cls, attr) return wraps(fn)(return_attr) return inner_cached_functionin a ModelClass:
@cached_function('_important_data') def get_important_data(self): return self.get_result_of_lots_of_queries()As said/written before, I almost have no experience with this, so please comment if you have anything to say about this solution.