Calculate property only once and use the result several times (different approaches)
Asked Answered
C

2

14

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
Computerize answered 13/7, 2017 at 20:12 Comment(2)
Nice question. Related SO question with strong favor for lru_cache. Because you use no arguments, intuitively, I'd stay with the vanilla try-catch.Bipolar
Does this answer your question? Caching class attributes in PythonAmias
A
13

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
Alethiaaletta answered 5/2, 2020 at 11:1 Comment(1)
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 questionTransistor
A
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.

Astronaut answered 13/7, 2017 at 20:38 Comment(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.Computerize

© 2022 - 2024 — McMap. All rights reserved.