0

Hi and thanks for checking, I am programming in Python and have a simple question to which I did not find an answer so far. I have a function output, which I need to use multiple times in an if-else statement.

Here an example code:

result = fooB(fooA()) if fooA()==*someComparison* else fooA()

Is there an elegant way to use such a function output multiple times without calling the function multiple times (computationally expensive) or having to temporarily save the output in a previous line?

M. Beining
  • 99
  • 1
  • 9
  • 2
    is `a = fooA()` not elegant enough? – Jonathan R Jun 08 '18 at 08:47
  • Is it ternary conditional operator you are looking for ? I don't understand you want to execute `fooB(fooA())` if the comparaison is true, else execute `fooA()` only ? – Hearner Jun 08 '18 at 08:47
  • 2
    Keep it simple and readable: `a = fooA(); if a == *some*: a = fooB(a)` – deceze Jun 08 '18 at 08:48
  • @Hearner: correct, this is what I want to do and yes I want to use ternary conditional operator. Actually what I could do is `[fooB(val) if val ==*someComparison* else val for val in [fooA()]][0]` but that looks quite ugly to me – M. Beining Jun 08 '18 at 08:58
  • @deceze: The problem is that code is part of a lambda function so I cannot save fooA() to a variable. However, if there is no way to do what I am looking for, I can of course replace the lambda with a real function – M. Beining Jun 08 '18 at 09:00
  • 1
    If `lambda` becomes "too small" for what you're trying to do within it, then yes, write a `def` function instead. – deceze Jun 08 '18 at 09:01
  • Like the other comments, keep it simple. As written in PEP 20: Explicit is better than implicit. Simple is better than complex. Complex is better than complicated. – lelabo_m Jun 08 '18 at 09:20

3 Answers3

2

As of yet, what you want is not possible because the python ternary operator only allows expressions and not statements. If you are curious about the difference I would recommend reading this amazing answer on the topic. But the important part for this question is that what you are asking for is an assignment (i.e. something like tmp = fooA(), as you correctly noted yourself), which is a statement.

Luckily, other people like you (and me) would appreciate the option to share temporary results within a single block, expression, or statement, which is the goal of PEP 572, due for python3.8 in late 2019. Given the PEP gets accepted.

You can try it already by compiling the POC from source. This is what it would look like:

>>> my_func = lambda: 'foo'
>>> tmp + 'bar' if (tmp := my_func()) == 'foo' else tmp
foobar
>>> tmp + 'bar' if (tmp := my_func()) == 'baz' else tmp
foo

Which is imo prettier and more straight forward than what you currently have to do with temporary variables:

>>> tmp = my_func()   # store the temp var
>>> if tmp == 'foo':  # the test
...    tmp += 'bar'   # the optional change
>>> tmp               # printing/reassigning the temp var
foobar
Arne
  • 17,706
  • 5
  • 83
  • 99
  • Nitpick: the ternary operator doesn't "only allow" expressions, it *is* an expression, called the [conditional expression](https://docs.python.org/3/reference/expressions.html#conditional-expressions). It can be used in other expressions and statements. – mkrieger1 Jun 08 '18 at 09:47
  • what I meant was that you can only use expressions within a conditional expression (for both condition and consequences), since OP asked for something that, today, only exists as a statement. Does your nit apply to that still? – Arne Jun 08 '18 at 09:52
  • I didn't want to use the term conditional expression because I use the word `expression` a lot already, and mean something very specific with it. – Arne Jun 08 '18 at 09:55
  • Actually I think I misunderstood what you meant, sorry. Yes, you're right, a conditional expression only allows expressions (because it is itself an expression). – mkrieger1 Jun 08 '18 at 09:56
  • 1
    You should at least propose an alternative working solution rather than suggesting people to compile a specific Python branch and enjoy a feature not even yet accepted... `result = fooA(); if result == bar: result = fooB(result)` looks fine to me. – Delgan Jul 02 '18 at 08:51
  • @Delgan I would have, if it weren't explicitly against OP's wishes. As it stands, there is no currently working solution for his problem -- which is exactly what I wrote. – Arne Jul 02 '18 at 09:07
  • 1
    I agree that your message answers straightly the author's question. But it is often good to expand an answer considering other people may come here without fanciful requirement like "don't use a temporary" (I would not call one-liner combining ternary, comparison and assignment expression "elegant" nor idiomatic Python). – Delgan Jul 02 '18 at 09:24
1

I'd just assign the value to a variable on a previous line, if it would be the same from all three calls.

As Hearner already wrote in the comments, your example is slightly confusing since you're not assigning the value of the ternary to anything, implying that fooB(x) is some function with side effects that you want to call, but not assign to a variable. In that case using the ternary operator isn't very pretty, I think.

In the case that fooB(fooA()) returns some value you want to assign if the comparison is true, or else assign fooA(), then

a = fooA()
b = fooB(a) if a == bar else a

is surely the way to go and not particularly inelegant.

In the case that fooB is some function with side effects which you only want to call if the comparison is true, you could write e.g.

a = fooA()
a == bar and fooB(a)

but not everyone likes that idiom so

a = fooA()
if a == bar:
    fooB(a)

might be preferrable…

Dronir
  • 866
  • 7
  • 10
0

If fooA() depends only on it's arguments, you could use memoization, but you'll still have the overhead of calling the function thrice (and the overhead of looking up the memo cache of course), so the answer is no, the only sensible solution here is definitly to store the result of the first call in an intermediate variable.

bruno desthuilliers
  • 75,974
  • 6
  • 88
  • 118