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.
March 20, 2009 17:59