2

I have to bear with a Python version < 2.5 (it is 2.4.3 for specifics)

It seems that ternary operators were introduces in Python starting at 2.5. For those who are not familiar, ternary operators in Python >= 2.5 look like this:

def do_ternary(flag):
    return "foo" if flag else "bar"

I'd like to know some solutions to emulate this in the early versions of Python. I can for sure do it with if ... else, but I'm looking for something more pythonic that I wouldn't be ashamed to put on some production-level code :)

Thanks for the help !

Charles Menguy
  • 40,830
  • 17
  • 95
  • 117
  • is your objection to if flag: return "foo" else: return "bar" that it's not stylish enough? or is there some concrete reason? – mfsiega Apr 15 '12 at 00:55
  • 1
    well the logic is fine but it's not a ternary operator. It's not really a question of "stylish" or not, it's more about code factorization, and writing clean code. I don't really like to have whole trees of if ... else statements in my code. – Charles Menguy Apr 15 '12 at 00:59
  • 2
    When you want to do something if a test is true, and something else otherwise, ``if ...: ... else: ...`` seems pretty reasonable. Don't confuse _terse_ and _readable_. Readable should always win out if you can't do both. – Gareth Latty Apr 15 '12 at 01:00
  • That's not really my question, I know we can do if ... else always, but I'm asking this specifically to avoid if ... else in the code (which in my mind make the code less readable). I'm sure you can do something similar using lambda functions but I haven't figured it out yet. – Charles Menguy Apr 15 '12 at 01:07
  • @linker I know, which is why I answered with a solution your way, and commented to say that really, it's a bad idea. It's not an answer, but it's the best *solution*. – Gareth Latty Apr 16 '12 at 11:12

4 Answers4

8

the correct way that does all of the things that if/else does is:

(condition and (yes_value,) or (no_value,))[0]

which does both the short circuiting and resolves the problem of when yes_value is itself falsey. Obviously, if you have reason to avoid this cludge, just do that; in your example, both conditions are constant expressions, so you can do:

{True: yes_value, False: no_value}[bool(condition)]

or more tersely:

(no_value, yes_value)[condition]

if you do need the short circut, but you're confident that the yes_value is never falsey, you can trim out the tuple:

 condition and yes_value or no_value

but that's probably only valid when the yes_value is actually a constant. If none of these suit your tastes or needs, just use a plain-ol if: statement, with an intermediate variable

if condition:
    result = yes_value
else:
    result = no_value
SingleNegationElimination
  • 151,563
  • 33
  • 264
  • 304
5

Actually I was looking on the web and found what seems like a really elegant pythonic solution:

def _if(test):
    return lambda alternative: \
               lambda result: \
                   [delay(result), delay(alternative)][not not test]()

def delay(f):
    if callable(f): return f
    else: return lambda: f

>>> fact = lambda n: _if (n <= 1) (1) (lambda: n * fact(n-1))
>>> fact(100)
93326215443944152681699238856266700490715968264381621468592963895217599993229915608941463976156518286253697920827223758251185210916864000000000000000000000000L

What do you think about this one? It looks pretty clean and easy to read in my opinion.

Charles Menguy
  • 40,830
  • 17
  • 95
  • 117
  • I think it's pretty f***ing cool :) I gotta say though, as far as readability goes, if... then statements are pretty darn readable. – Joel Cornett Apr 15 '12 at 01:30
  • 1
    Yeah I agree that if ... else are the most readable, but I'm just trying to be creative and have a one-liner to do the trick :) So far I believe this one and @TokenMacGuy's are the ones I'm considering most. – Charles Menguy Apr 15 '12 at 01:35
  • I think this is possibly the least readable code I've seen to do this. While it's a cool bit of code, I really don't feel it actually provides any benefit to the reader or writer. – Gareth Latty Apr 16 '12 at 11:13
4

A common trick is to use list-indexing, since False/True turn into 0/1 when an integer is required. In case the test may be false-y or truth-y rather than a boolean, its good practice to first ensure the test is a boolean:

["bar", "foo"][bool(flag)]

will produce the same output as the ternary in your question.

Edit: Dougal points out that this may behave slightly differently from the ternary, because both the true and false values will be evaluated, which may have side-effects.

Aaron Dufour
  • 17,288
  • 1
  • 47
  • 69
  • that's a good alternative, but i feel it's a bit lacking in terms of readability. One of the advantages of ternary operations is that it's straightforward to read, and while this works fine, I find it's also complexifying the code. – Charles Menguy Apr 15 '12 at 01:03
  • This also doesn't short-circuit evaluation. – Danica Apr 15 '12 at 01:08
1

The classic 'trick' used to do this is:

test and true_value or false_value

This works as and and or work like so in python:

x or y   ->    if x is false, then y, else x
x and y  ->    if x is false, then x, else y

Source

This means that we get the roughly the same result - so long as true_value evaluates to True - so, for example, the following would not work:

flag and [] or "bar"

As [] evaluates to False.

I'd still argue that this is less readable than simply using an if/else block, as unless you are familiar with it, it's unclear.

So I'd advise using:

if test:
    return true_value
else:
    return false_value

(replacing return with assignment or whatever where needed).

Gareth Latty
  • 86,389
  • 17
  • 178
  • 183
  • 5
    Note that this won't work as expected if `true_value` is something that evaluates to `False`, e.g. in `flag and [] or "bar"`. – Danica Apr 15 '12 at 00:55
  • @Dougal +1. I'll add that note in - I never liked this myself and so never used it, forgot about that problem. – Gareth Latty Apr 15 '12 at 01:02