It's a bird, it's a plane, it's

Everything here is my opinion. I do not speak for your employer.
March 2009
April 2009

2009-03-20 »

The ternary operator in python

You might have heard of the so-called "ternary operator" in C. Or maybe you haven't; someone asked me about it as one of those useless skill-testing questions (which actually just checks trivia knowledge, not skill) at my first ever job interview, and I hadn't heard that term before.

Before that fateful interview,(1) I used to just call it the "question-mark colon" operator. It is, as far as I know, the only operator in C that really does take three parameters. Or you could argue that it's really two binary operators stuck together. You know what? Nobody really cares. I'm just trying to talk about the questionmark colon in as many different ways as I can so that Google can find this entry in case someone needs it later.

Phew. So as I was saying, the question colon operator looks something like this:

    int x = really ? 10000 : -1;

Or, if you're dramatic and participating in an obfuscated code contest, you could write this:

    int x = really?! no: !yes; maybe. ok();

Okay, whatever, I have a cold and I'm delerious. I'm pretty sure you can make that compile. But for the love of God, don't.

The point of the ternary operator is it checks the truthiness of the first parameter, and if it's true, returns the second parameter. Otherwise, it returns the third parameter. It's a short way of saying "if really then x = 10000 else x = -1", and it's useful in a number of space-saving contexts.

This is all fine. Lots of languages, in the name of being "friendlier," omitted the concept of a ternary operator, because it's scary the first time you see one, and people need to be coddled and protected lest they actually learn something. Perl is not one of those languages, for example.

But python is. They have resisted including a ternary operator. Looking for evidence of one online will result in people angrily telling you the ternary operator is scary and confusing and should be avoided at all costs. Geez, write nice-looking code already!

Well, I'm sorry, but combined with the way python forces me to put even trivial if statements on more than one line, this is a pretty insulting thing to leave out.

Now, if you've been following the not-so-recent python news like I have been not-so-recently, you might know that python 2.5 added a handy feature that sort of works like the ternary operator:

    x = 10000 if really else -1

To that I say... WHAT THE HECK IS WRONG WITH YOU PEOPLE? Did you really just put the condition in the middle of a sentence? Did you really just define "if" as having a higher precedence than "="?

I'm sure the python people thought they were very clever when they came up with this; they were, I assume, copying the feature in perl that lets you say things like "$x = 5 unless $really" and so on. But they've missed the point; perl can have a feature like that because sometimes it makes sense. Nobody tries to claim that this is the way to do an inline conditional in perl; they have the real ?: operator and this one, and several other weird things. And you can always use "if" instead of "unless."

Phew.

Like I said, I'm delerious. Please forgive my rambling.

All this was just to say that:

There was already a perfectly good ternary operator in python

Oh.

Somehow, in all my searching, I didn't find anybody who told me this; in fact, I stole it from perl too.

    x = really and 10000 or -1

Why does that work?

Well, in fact, it's kind of neat. In C, it wouldn't work, because while C's type coercion rules are almost the greatest type coercion rules ever invented by programmers, they missed one trick. In C, the result of a boolean operator (like || or &&) is always a boolean, either true or false. So if you say "0 || 5", you get 1, which is the canonical value for truth.(2)

In perl, they realized that wasn't quite optimal; any non-zero value is true, after all, so there's no reason to give one such special status. So in perl, they decided that "0 || 5" would be 5. If you check to see if 5 is true, it is, so the net result is the same. The same, that is, until you write something like this:

    $x = $really && 10000 || -1;

If $really is true (which means nonzero), then you need to evaluate the other side of the && operation to make sure that the whole thing is true. Since the other side is 10000, which is true, then the whole thing is true, and perl just leaves behind the most recent number it used, which was 10000. On the other hand, if $really is false, then the overall statement can only be true if the || clause is true. In fact, we don't even care if the || clause is true, but it'll be the last thing perl evaluates, so whether it's true or not, that value is returned in the false case. So this is pretty much the same as writing "$x = $really ? 10000 : -1".

BEWARE. Do not write "$x = $really and 10000 or -1;" in perl. It doesn't do what you think it does, because the "and" operator has lower precedence than the "=" operator, while the "&&" operator has higher precedence. perl kind of missed the sanity boat on that one.

Anyway, the python designers, liking minimal syntax and maximal type dynamicness, stole this feature, so you can write:

    x = really and 10000 or -1

And it'll work. Note that in python, the "and" operator has higher precedence than "=", as it should, unlike in perl.

There's only one little catch: the "true" side has to evaluate to true. This won't work:(3)

    x = really and 0 or 1

Because even if "really" is true, you'll get 1 instead of 0. Oops. Luckily this comes up virtually not at all in real life, since if you wanted to write the above, you would have just written "x = not really". Which is much funnier.

Side note

Visual Basic doesn't have expression short circuiting, which is only one of many reasons why this trick won't work there. (VB.Net does, but you have to use new words, not "or" or "and.")

Footnote

(1) I got the job anyway, because I redeemed myself by knowing that "volatile" can apply either to the pointer or what it points at. Try not to think about that too much. It hurts.

(2) Except in the Unix shell, where 0 is the canonical value for truth. That actually makes a lot of sense, since there's generally only one way to be right, but lots of ways to be wrong. But 0 just doesn't feel true enough.

(3) I'm still waiting for perl6, so I can write "$x = $really and (0 is true) or (1 is false);" Of course, I won't need to, because they have a ternary operator.

::li,nv

I'm CEO at Tailscale, where we make network problems disappear.

Why would you follow me on twitter? Use RSS.

apenwarr on gmail.com