2

I want to conditionally lazy evaluate a function with some heavy computations:

def fun(a):
    yield a + 1
    yield a * 2

Is it possible to write this exact function but with 1 line in the function body? Something like this:

def fun(a):
    yield from (a+1, a*2)

But in the code snippet above I think the tuple will be evaluated eagerly? I only want to evaluate a*2 if necessary.

Kevin
  • 3,096
  • 2
  • 8
  • 37
  • 5
    Why do you care how many lines you use? – juanpa.arrivillaga May 25 '21 at 16:02
  • 1
    Do you mind clarifying the question in respect to JonSG's answer and the discussion under it? – Tomerikoo May 25 '21 at 16:23
  • 1
    ```a+1``` and ```a*2``` are the heavy computations. But I think using the yield from on a generator is the way to go here if there are multiple heavy computations. The arguments to the heavy computations can be encapsulated and are not hard to compute per say. – Kevin May 25 '21 at 16:33
  • I think OP has inadvertently oversimplified their task. Because if you have two separate simple things to do, doing them on two lines is best. I suggest either keeping it as you show in your first snippet or provide more context, please. – ggorlen May 25 '21 at 19:07
  • Maybe you wanted a list with lazily evaluated items like in this answer: ["Lazy" list item evaluation in Python](https://stackoverflow.com/a/53163634/320437) – pabouk - Ukraine stay strong May 03 '22 at 16:48

3 Answers3

3

Sure, you can separate simple statements on a single line by a semicolon:

def fun(a):
    yield a + 1; yield a * 2

But why do you care how many lines it takes? Just go with your original version, that is the most Pythonic one. At the very least, the above is against PEP-8 style guidelines

juanpa.arrivillaga
  • 88,713
  • 10
  • 131
  • 172
0

You can try this

def fun(*args):
    a= yield from  (x+ 1 for x in args);yield a *2

b=fun(12,2,1,2,3,4)
next(b) # (12 +1)
next(b) # (2 +1)

However, if you want to directly use a* 2 with the next value

b.send(next(b)) #(1 *2)
Ade_1
  • 1,480
  • 1
  • 6
  • 17
0

This is possible, but I think I like your original multi-line answer better:

Here foo() is your expensive part of your original fun() calculation base on inputs that might be provided say in a loop by fun().

It was not clear from my original example what the distinction between foo() and fun() was as my fun() included the original a+1 "expensive" calculation.

I hope to make this more clear by passing (a, 1) into foo() as paramters where foo() performs the expensive part in a lazy manner.

import time

def foo(a, i):
    # this is expensive
    time.sleep(5)
    return a + i

def fun(a):
    yield from (foo(*x) for x in [(a,1), (a,2)])

for x in fun(10):
    print(x)

This is lazy on foo()

JonSG
  • 10,542
  • 2
  • 25
  • 36
  • 1
    The list `[a + 1, a + 2]` is evaluated eagerly... – Tomerikoo May 25 '21 at 16:10
  • 1
    @Tomerikoo, The `parameters` passed to foo are evaluated eagerly. `foo()` is not. Presumably, figuring out the parameters to pass to `foo()` is relatively inexpensive – JonSG May 25 '21 at 16:11
  • It is not clear to me from the question what is `foo` – Tomerikoo May 25 '21 at 16:14
  • 1
    @Tomerikoo `foo()` would be "some heavy computations" to be performed repeatedly in a lazy manner per the question. `foo()` though is not `fun()` – JonSG May 25 '21 at 16:16
  • 1
    To my understanding, `a+1` and `a*2` are a mock example of the heavy computations – Tomerikoo May 25 '21 at 16:17
  • Yeah, I understand what you are getting at now but I don't think we can assume there is a function `foo` necessarily, but you could expand it out to working with multiple separate functions I guess – juanpa.arrivillaga May 25 '21 at 16:18
  • @Tomerikoo Hopefully that edit makes my intentions a little clearer. – JonSG May 25 '21 at 16:27
  • Yes this is a bit clearer and I can see what you're trying to do here. I think that the main problem is that the question is a bit unclear and could use some more details – Tomerikoo May 25 '21 at 17:35
  • 1
    @JonSG Yes you can easily extend the code to allow specifying a function instead of the hardwired `foo()`. Here is an example with `foo()` and `bar()`: `yield from (params[0](*params[1:]) for params in [(foo, a, 1), (bar, a, 2)])` – pabouk - Ukraine stay strong May 03 '22 at 14:10