13

I'm trying to use the result of a class method several times without doing the heavy calculations required to obtain the result.

I am seeing the following options. Which ones do you think is the right one, or more pythonic?

What are the advantages and disadvantages of each one?

Try/Except approach

class Test:
    def __init__(self, *args):
        # do stuff

    @property
    def new_method(self):
        try:
            return self._new_property
        except AttributeError:
            # do some heavy calculations
            return self._new_property

lru_cache approach

from functools import lru_cache

class Test:
    def __init__(self, *args):
        # do stuff

    @property
    @lru_cache()
    def new_method(self):
        # do some heavy calculations
        return self._new_property

Django's cache_property approach

from django.utils.functional import cached_property

class Test:
    def __init__(self, *args):
        # do stuff

    @cached_property
    def new_method(self):
        # do some heavy calculations
        return self._new_property
Martin Thoma
  • 124,992
  • 159
  • 614
  • 958
Eduardo
  • 687
  • 1
  • 6
  • 24
  • Nice question. [Related SO question](https://stackoverflow.com/questions/815110/is-there-a-decorator-to-simply-cache-function-return-values) with strong favor for lru_cache. Because you use no arguments, intuitively, I'd stay with the vanilla try-catch. – agtoever Jul 13 '17 at 20:25
  • Does this answer your question? [Caching class attributes in Python](https://stackoverflow.com/questions/4037481/caching-class-attributes-in-python) – Georgy May 04 '20 at 17:20

2 Answers2

10

Python 3.8 update: You can now use functools.cached_property

from functools import cached_property

class Test:
    def __init__(self, *args):
        # do stuff

    @cached_property
    def new_method(self):
        # do some heavy calculations
        return self._new_property
Martin Thoma
  • 124,992
  • 159
  • 614
  • 958
  • 2
    Something worth mentioning from the docs: "The mechanics of `cached_property()` are somewhat different from `property()`. A regular `property` blocks attribute writes unless a setter is defined. In contrast, a `cached_property` allows writes." So in this respect, the answer differs from some of the alternatives proposed in the question – Silverfish Nov 20 '22 at 22:10
4
  1. Try/except is simple and readable, but one day you would want to cache another property, right? So one day you will write your own cached property probably.

  2. lru_cache it is a good idea to use standard library, but as you don't need lru cache, it is an overhead probably.

  3. Django's cache_property works exactly as you want and it is pretty simple. It has analogue in werkzeug (so Flask users familiar with it too), it is easy to find a sources, so probably it is a good choice for you.

Alex Pertsev
  • 931
  • 4
  • 13
  • 1
    Yes, **Django's cache_property** is very simple. I did some test and its very fast, 2.5 faster than **try/except**. If there is no disadvantage in using it, I think I will use it. I have several properties in my class, and perhaps the code will be cleaner. – Eduardo Jul 13 '17 at 20:48