1
\$\begingroup\$

Suppose I have some expensive function expensive_computation which takes a long time to compute but it always returns the same value.

I want to avoid paying the cost of computing this function because it may never be called.

Here is my solution to this, is there a more obvious or clean way to do it? Or maybe even some function from the standard library?

def lazy(function):

  internal_state = None

  def lazy_evaluation():
    nonlocal internal_state
    if internal_state is None:
      internal_state = function()
    return internal_state

  return lazy_evaluation

@lazy
def expensive_computation():
  print("Working ...")
  return "that was hard"

print(expensive_computation())
print(expensive_computation())

Which prints:

Working ...
that was hard
that was hard
\$\endgroup\$
  • \$\begingroup\$ Suppose this isn't real code, then it's undeniably off-topic here. Is only lazy up for review, and do you want any and all facets of your code reviewed? Please ensure your question is otherwise on-topic. \$\endgroup\$ – Peilonrayz May 3 at 16:31
  • \$\begingroup\$ Any review concerning the implementation of lazy or the use of such a construct is what I'm looking for. I'm not sure what you mean by "real code", it's some working code, where I replaced every piece on which I don't want advice on by dummy code (I don't need advice on the content of expensive_computation, that's why I don't provide its implementation). On the other hand, the implementation of lazy is the exact one I intend to use. I added the output to make clear what behaviour I expect (because I noticed the term "lazy evaluation" is actually broader than this use case). \$\endgroup\$ – cglacet May 3 at 16:42
  • \$\begingroup\$ Please see What to do when someone answers. I have rolled back Rev 2 → 1. \$\endgroup\$ – 200_success May 3 at 17:34
1
\$\begingroup\$
    if internal_state is None:
      internal_state = function()

What if function() returns None?


Or maybe even some function from the standard library?

functools.lru_cache

\$\endgroup\$
  • \$\begingroup\$ So how do you change the code with regard to the first point if None is a valid return? \$\endgroup\$ – Peilonrayz May 3 at 16:48
  • \$\begingroup\$ My function never return None, but in the general case I guess that you would just have a boolean marker beside the internal_state, so you can tell if the function has been evaluated or not, but that's just a guess and probably not the best solution either. Concerning the cache solution I'll have a look at it. \$\endgroup\$ – cglacet May 3 at 17:09
  • \$\begingroup\$ @Peilonrayz, the obvious approach would be to use a separate Boolean variable. An alternative, which I consider hackier, would be to not have a separate variable for internal_state at all: result = function(); function = lambda () => result; return result \$\endgroup\$ – Peter Taylor May 3 at 17:18

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.