是否有一个条件运算符?

If Python does not have a ternary conditional operator, is it possible to simulate one using other language constructs?

转载于:https://stackoverflow.com/questions/394809/does-python-have-a-ternary-conditional-operator

You might often find

cond and on_true or on_false

but this lead to problem when on_true == 0

>>> x = 0
>>> print x == 0 and 0 or 1 
1
>>> x = 1
>>> print x == 0 and 0 or 1 
1

where you would expect for a normal ternary operator this result

>>> x = 0
>>> print 0 if x == 0 else 1 
0
>>> x = 1
>>> print 0 if x == 0 else 1 
1

Simulating the python ternary operator.

For example

a, b, x, y = 1, 2, 'a greather than b', 'b greater than a'
result = (lambda:y, lambda:x)[a > b]()

output:

'b greater than a'

An operator for a conditional expression in Python was added in 2006 as part of Python Enhancement Proposal 308. Its form differ from common ?: operator and it's:

<expression1> if <condition> else <expression2>

which is equivalent to:

if <condition>: <expression1> else: <expression2>

Here is an example:

result = x if a > b else y

Another syntax which can be used (compatible with versions before 2.5):

result = (lambda:y, lambda:x)[a > b]()

where operands are lazily evaluated.

Another way is by indexing a tuple (which isn't consistent with the conditional operator of most other languages):

result = (y, x)[a > b]

or explicitly constructed dictionary:

result = {True: x, False: y}[a > b]

Another (less reliable), but simpler method is to use and and or operators:

result = (a > b) and x or y

however this won't work if x would be False.

A possible workaround is to make x and y lists or tuples as in the following:

result = ((a > b) and [x] or [y])[0]

or:

result = ((a > b) and (x,) or (y,))[0]

If you're working with dictionaries, instead of using a ternary conditional, you can take advantage of get(key, default), for example:

shell = os.environ.get('SHELL', "/bin/sh")

Source: ?: in Python at Wikipedia

Does Python have a ternary conditional operator?

Yes. From the grammar file:

test: or_test ['if' or_test 'else' test] | lambdef

The part of interest is:

or_test ['if' or_test 'else' test]

So, a ternary conditional operation is of the form:

expression1 if expression2 else expression3

expression3 will be lazily evaluated (that is, evaluated only if expression2 is false in a boolean context). And because of the recursive definition, you can chain them indefinitely (though it may considered bad style.)

expression1 if expression2 else expression3 if expression4 else expression5 # and so on

A note on usage:

Note that every if must be followed with an else. People learning list comprehensions and generator expressions may find this to be a difficult lesson to learn - the following will not work, as Python expects a third expression for an else:

[expression1 if expression2 for element in iterable]
#                          ^-- need an else here

which raises a SyntaxError: invalid syntax. So the above is either an incomplete piece of logic (perhaps the user expects a no-op in the false condition) or what may be intended is to use expression2 as a filter - notes that the following is legal Python:

[expression1 for element in iterable if expression2]

expression2 works as a filter for the list comprehension, and is not a ternary conditional operator.

Alternative syntax for a more narrow case:

You may find it somewhat painful to write the following:

expression1 if expression1 else expression2

expression1 will have to be evaluated twice with the above usage. It can limit redundancy if it is simply a local variable. However, a common and performant Pythonic idiom for this use-case is to use or's shortcutting behavior:

expression1 or expression2

which is equivalent in semantics. Note that some style-guides may limit this usage on the grounds of clarity - it does pack a lot of meaning into very little syntax.

For Python 2.5 and newer there is a specific syntax:

[on_true] if [cond] else [on_false]

In older Pythons a ternary operator is not implemented but it's possible to simulate it.

cond and on_true or on_false

Though, there is a potential problem, which if cond evaluates to True and on_true evaluates to False then on_false is returned instead of on_true. If you want this behavior the method is OK, otherwise use this:

{True: on_true, False: on_false}[cond is True] # is True, not == True

which can be wrapped by:

def q(cond, on_true, on_false)
    return {True: on_true, False: on_false}[cond is True]

and used this way:

q(cond, on_true, on_false)

It is compatible with all Python versions.

You can index into a tuple:

(falseValue, trueValue)[test]

test needs to return True or False.
It might be safer to always implement it as:

(falseValue, trueValue)[test == True]

or you can use the built-in bool() to assure a Boolean value:

(falseValue, trueValue)[bool(<expression>)]

expression1 if condition else expression2

>>> a = 1
>>> b = 2
>>> 1 if a > b else -1 
-1
>>> 1 if a > b else -1 if a < b else 0
-1

For versions prior to 2.5, there's the trick:

[expression] and [on_true] or [on_false]

It can give wrong results when on_true has a false boolean value.1
Although it does have the benefit of evaluating expressions left to right, which is clearer in my opinion.

1. Is there an equivalent of C’s ”?:” ternary operator?

Ternary Operator in different programming Languages

Here I just try to show some important difference in ternary operator between a couple of programming languages.

Ternary Operator in Javascript

var a = true ? 1 : 0;
# 1
var b = false ? 1 : 0;
# 0

Ternary Operator in Ruby

a = true ? 1 : 0
# 1
b = false ? 1 : 0
# 0

Ternary operator in Scala

val a = true ? 1 | 0
# 1
val b = false ? 1 | 0
# 0

Ternary operator in R programming

a <- if (TRUE) 1 else 0
# 1
b <- if (FALSE) 1 else 0
# 0

Ternary operator in Python

a = 1 if True else 0
# 1
b = 1 if False else 0
# 0

Now you can see the beauty of python language. its highly readable and maintainable.

you can do this :-

[condition] and [expression_1] or [expression_2] ;

Example:-

print(number%2 and "odd" or "even")

This would print "odd" if the number is odd or "even" if the number is even.


The result :- If condition is true exp_1 is executed else exp_2 is executed.

Note :- 0 , None , False , emptylist , emptyString evaluates as False. And any data other than 0 evaluates to True.

Here's how it works:

if the condition [condition] becomes "True" then , expression_1 will be evaluated but not expression_2 . If we "and" something with 0 (zero) , the result will always to be fasle .So in the below statement ,

0 and exp

The expression exp won't be evaluated at all since "and" with 0 will always evaluate to zero and there is no need to evaluate the expression . This is how the compiler itself works , in all languages.

In

1 or exp

the expression exp won't be evaluated at all since "or" with 1 will always be 1. So it won't bother to evaluate the expression exp since the result will be 1 anyway . (compiler optimization methods).

But in case of

True and exp1 or exp2

The second expression exp2 won't be evaluated since True and exp1 would be True when exp1 isn't false .

Similarly in

False and exp1 or exp2

The expression exp1 won't be evaluated since False is equivalent to writing 0 and doing "and" with 0 would be 0 itself but after exp1 since "or" is used, it will evaluate the expression exp2 after "or" .


Note:- This kind of branching using "or" and "and" can only be used when the expression_1 doesn't have a Truth value of False (or 0 or None or emptylist [ ] or emptystring ' '.) since if expression_1 becomes False , then the expression_2 will be evaluated because of the presence "or" between exp_1 and exp_2.

In case you still want to make it work for all the cases regardless of what exp_1 and exp_2 truth values are, do this :-

[condition] and ([expression_1] or 1) or [expression_2] ;