3

I have a class that takes only one input argument. This value will then be used to compute a number of attributes (only one in the following example). What is a pythonic way if I wanted to take the computation place only if I call the attribute. In addition, the result should be cached and attr2 must not be set from outside the class.

class LazyInit:
    def __init__(self, val):
        self.attr1 = val
        self.attr2 = self.compute_attr2()

    def compute_attr2(self):
        return self.attr1 * 2  # potentially costly computation


if __name__ == "__main__":
    obj = LazyInit(10)

    # actual computation should take place when calling the attribute
    print(obj.attr2)
1
  • 1
    Define a getter (property docs). Commented Sep 29, 2021 at 15:21

2 Answers 2

5

As hinted above, just use @cached_property decorator.

from functools import cached_property

class LazyInit():
    ...
    @cached_property
    def attr2(self):
        return <perform expensive computation>

Olvin Roght correctly points out that this solution doesn't make attr2 read-only the way that @property does. If that is important to you, another possibility would be to write:

    ...
    @property
    def attr2(self):
        return self.__internal_attr2()

    @functools.cached
    def __internal_attr2(self):
        return <perform expensive calculation>

In any case, Python provides libraries to help you ensure that a value is only calculated once. It is better to use them than to try and write your own.

Sign up to request clarification or add additional context in comments.

4 Comments

As I said in comment under accepted answer, it could be an option. Quote from docs: "The mechanics of cached_property() are somewhat different from property()". If you need a property to be read-only or to define custom setter cached_property is not your choice.
This is definitely worth looking at! Thanks!
@OlvinRoght. You are absolutely right. I edited the solution above to deal with the case that the user wants read-only.
I was wondering that if the class attribute is different with instance property. What the answer solved is that the time-consuming __internal_attr2 is calculated once for each instance of LazyInit but not for all different instances of LazyInit.
3

Make attr2 a property, not an instance attribute.

class LazyInit:
    def __init__(self, val):
        self.attr1 = val
        self._attr2 = None

    @property
    def attr2(self):
        if self._attr2 is None:
            self._attr2 = self.compute_attr2()
        return self._attr2

_attr2 is a private instance attribute that both indicates whether the value has been computed yet, and saves the computed value for future access.

1 Comment

In some cases it could be a good option to use cached_property decorator.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.