views:

758

answers:

12
def val():
    var = float(raw_input("Age:"))
    status = ("Working","Retired")[var>65]
    print "You should be:",status

I was looking on how the ternary operator is implemented in python. I found the code mentioned above as a substitute (from what i have read, found that the ternary operator does not exist, I will be happy to know more about it.). I couldn't understand how the code works, can anyone explain me how actually the code is working. Am also interested to know why the ternary operator doesn't exist Any references or links about this will be more useful.

ps: The version am using is 2.6.4 on Windows Vista

+7  A: 

Python (2.5 and above) does indeed have a syntax for what you are looking for:

x = foo if condition else bar

If condition is True, x will be set to foo, otherwise it will be set to bar.

Examples:

>>> age = 68
>>> x = 'Retired' if age > 65 else 'Working'
>>> x
'Retired'
>>> age = 35
>>> y = 'Retired' if age > 65 else 'Working'
>>> y
'Working'
TM
Just a heads up that this is only available in version 2.5 or later. There is nothing similar in older versions.
Pace
@Pace yes, although the asker mentions they are using 2.6.4. Just added a note for clarity though.
TM
A: 

In Python 2.6 and up:

print "You should be {0}.".format("retired" if var>65 else "working")

In Python 3.1 and up:

print ("You should be {}.".format("retired" if var>65 else "working"))
Tim Pietzcker
In your first example, Python 2.5 doesn't have that style of string formatting.
Brian Neal
Ah, thanks; I took this info from Pace's comment to a different answer - you're right, `.format()` was introduced in 2.6.
Tim Pietzcker
+6  A: 

because True casts to 1 and False casts to 0 so if var = 70

("Working","Retired")[var>65]

becomes

("Working", "Retired")[1]

a nice little shortcut ... but I find it can be a little confusing with anything but a simple condition, so I would go with TM's suggestion

"Retired" if var > 65 else "Working"
mozillalives
A: 

in the code that you posted the following line is emulating ternary:

status = ("Working","Retired")[var>65]

here tuple ("Working","Retired") accessed with an index [var>65] which evaluates to either True (1) or False (0). When it's accessed with index 0, status will be 'Working'; if index is 1 then it'll be `Retired'. It's a fairly obscure way to do conditional assignment, use the normal ternary syntax that was introduced in py2.5 as was said.

SilentGhost
+20  A: 

Python has a construct that is sort of like the ternary operator in C, et al. It works something like this:

my_var = "Retired" if age > 65 else "Working"

and is equivalent to this C code:

my_var = age > 65 ? "Retired" : "Working";

As for how the code you posted works, let's step through it:

("Working","Retired")

creates a 2-tuple (an immutable list) with the element "Working" at index 0, and "Retired" at index 1.

var>65

returns True if var is greater than 65, False if not. When applied to an index, it is converted into 1 (True) or 0 (False). Thus, this boolean value provides an index into the tuple created on the same line.

Why hasn't Python always had a ternary operator? The simple answer is the Guido van Rossum, the author of Python, didn't like/didn't want it, apparently believing that it was an unnecessary construct that could lead to confusing code (and anyone who's seen massively-nested ternary operators in C can probably agree). But for Python 2.5, he relented and added the grammar seen above.

mipadi
I recall sitting through an OSCON *keynote* where Guido talked about why he *didn't* put the ternary operator into Python. Think about it, a large audience of diverse developers and techies and the speaker is going on about the minute details of a language specific feature that was old news 30 years ago and *didn't implement*. No attempt to make a broader point. The most boring keynote I've ever experienced.
Schwern
A: 

this is the form with the python ternary operator

def val():
    var = float(raw_input("Age:"))
    status = "Retired" if var > 65 else "Working"
    print "You should be:",status

the code you showed is a bit tricky: it creates a two elements tuple whose elements are at position 0 and 1. to select the right element it uses a condition which return a boolean but booleans in python are integers so you can use it as special indexes (they can be either 0 or 1).

mg
Oops. Your ternary condition is reversed. A good example of why you'd choose to use another construct for clarity.
jmanning2k
Unfortunately simple boolean logic like this is ALWAYS easy to inadvertently flip, no matter how it's formatted. I don't think that the ternary really has anything to do with it.
Toji
jmanning2k: I reverted the answer to its initial form, since it was already correct. Think of it as an English phrase: "You should be retired if older than 65, else working."
ΤΖΩΤΖΙΟΥ
A: 

There was originally no ternary operator because "Explicit is better than implicit", and it was seen as unpythonic. I don't like python's ternary op too much, either, but it exists:

x = foo if condition else bar

as shown by TM.

As for status = ("Working","Retired")[var>65], var > 65 returns a boolean value: either True or False; however, Python treats boolean types quite weakly: True is 1 and False is 0 in some contexts. You can check it out by doing >>> True == 1.

Tordek
A: 
status = ("Working","Retired")[var>65]

This line works as a ternary operator because the expression var>65 returns 1 or 0, depending on whether var is bigger than 65 or not. So if var>65, then the line becomes this:

status = ("Working","Retired")[1]

that is, the second element of the sequence ("Working","Retired"). It looks odd but not if you write it like this instead:

status_sequence = ("Working","Retired")
status = status_sequence[1]

so status = "Retired".

Similarly, if var<=65 then it becomes

status = ("Working","Retired")[0]

and status = "Working".

Paul Stephenson
A: 

Only the "status =" line of that code implements something like the ternary operator.

status = ("Working","Retired")[var>65]

This creates a two-element tuple, with strings 'Working' at index 0, and 'Retired' at index 1. Following this, it indexes into that tuple to pick one of the two items, using the results of the expression var > 65.

This expression will return True (equivalent to 1, thus picking 'Retired') if the value of var is greater than 65. Otherwise it will return False (equivalent to 0, thus picking 'Working').

There is a key difference between this approach and the ternary operator, however, although it doesn't matter in your particular example. With the tuple-indexing approach, both values are evaluated but only one is returned. With the ternary operator, only one of the two values is actually evaluated; this is referred to as "short-circuit" behaviour. It can matter in cases like this:

status = funcA() if var > 65 else funcB()
status = (funcB(), funcA())[var > 65]

In the first case, either funcA() is called or funcB() is called, but never both. In the latter case, both are called first, and the results are stored in the tuple -- then only one is picked and the tuple is discarded.

This is especially important to understand if either funcA() or funcB() have "side-effects", meaning they change other data as they execute.

Peter Hansen
A: 

trying to give a complete answer based on the answers given here.

the way you found (please don't use this one because it is not very readable):

def val():
    var = float(raw_input("Age:"))
    status = ("Working","Retired")[var>65]
    print "You should be:",status

using the python 2.5+ syntax:

def val():
    var = float(raw_input("Age:"))
    status = "Working" if var>65 else "Retired"
    print "You should be:",status

using the other common method still preferred by some people:

def val():
    var = float(raw_input("Age:"))
    status = var>65 and "Working" or "Retired"
    print "You should be:",status

i personally tend to use the last since the order of the operands is the same as the C ternary operator.

EDIT: found some problems with the last approach (thx Roberto Bonvallet).
from wikipedia:

this code would break if op1 could be a "falsy" value (None, False, 0, an empty sequence or collection, …) as the expression would return op2 (whether it was truthy or falsy) instead of the (falsy) op1

so my final suggestion would be to use the 2.5+ ternary operator since it is simple, readable and offers short-circuit behavior.

João Portela
I can't believe people keep suggesting the and-or hack. They're not equivalent, since the and-or trick has some nasty corner cases where it doesn't work as expected. (I'm not downvoting you because you're answering for the sake of completitude, but I'm commenting also for the sake of completitude to warn all you to DON'T DO IT.)
Roberto Bonvallet
The and-or hack has one sole advantage, which is that it allows short-circuit behaviour. The risks are generally too great to use it though (the biggest being that the programmer doesn't understand it, and the second biggest being that the *next* programmer won't understand it).
Peter Hansen
+2  A: 

indexing into a list

The use of

[expression_when_false, expression_when_true][condition] # or
(expression_when_false, expression_when_true)[condition]

takes advantage of the fact that in Python True equals (but isn't!) 1 and False equals (but isn't!) 0. The expression above constructs a list of two elements, and uses the result of condition to index in the list and return only one expression. The drawback of this method is that both expressions are evaluated.

and-or shortcuts

Since the creation of Python, there was a form of this operation:

condition and expression_when_true or expression_when_false

This takes a shortcut and evaluates only one expression, but has a bug-prone drawback: the *expression_when_true* must not evaluate to a non-true value, otherwise the result is *expression_when_false*. and and or are "short-circuiting" in Python, and the following rules apply:

a and b #→ a if a is false, else b
a or b  #→ a if a is true, else b

If condition is false, then *expression_when_true* is never evaluated and the result is *expression_when_false*. OTOH, if condition is true, then the result is the result of (*expression_when_true* or *expression_when_false*); consult the table above.

ternary conditional operator

Of course, since Python 2.5, there is a ternary conditional operator:

expression_when_true if condition else expression_when_false

The strange (if you are accustomed to the C-like ternary conditional operator) order of the operands is attributed to many things; the general intention is that condition should be true most of the time, so that the most common output comes first and is most visible.

ΤΖΩΤΖΙΟΥ
+1 Best answer!
jetxee
A: 

Short-circuit boolean expressions

There is also an option to short-circuit logical operations:

>>> (2+2 == 4) and "Yes" or "No"
'Yes'
>>> (2+2 == 5) and "Yes" or "No"
'No'

In your example:

>>> (int(raw_input("Age: ")) > 65) and "Retired" or "Working"
Age: 20
'Working'
>>> (int(raw_input("Age: ")) > 65) and "Retired" or "Working"
Age: 70
'Retired'

Read more about this technique in Charming Python: Functional Programming in Python, Part 1.

jetxee