Best way to do ternary conditionals in Python < 2.5 - python

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 !

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

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.

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.

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).

Related

Simple return function, what is more Pythonic? [duplicate]

This question already has answers here:
It is more efficient to use if-return-return or if-else-return?
(9 answers)
Closed 4 years ago.
Simple question that I can't find the answer for.
Take the function:
The below looks nice, i.e. more readable, given the else and the aligned spacing between the two return statement. However the else is completely pointless, and that makes it feel a little dirty.
def get_val(self):
if long_var < other_long_var and var < other_var:
return true
else:
return false
Note the two conditions and the long line, this is to express that the following is not applicable in this example:
return true if this else false
However the below is not as readable, but only slightly. It is more elegant as the useless else is omitted.
def get_val(self):
if x:
return true
return false
Is there a Pythonic bias for either of these styles?
If not, is it purely down to the developer?
EDIT: To be clear, it is an example function. I was trying to express the need for a function that has a condition and returns true of false.
This is a purely stylistic question
If you're just trying to find if the value of a paramater is either True or False, you can use python's built-in function bool.
x = ""
bool(x)
>>> False
x = "something"
bool(x)
>>> True
Also, you should check the official docs:
https://docs.python.org/3.6/library/functions.html
Hope it helped.
In such short cases, I would prefer:
def get_val(self):
return x
it is readable for experienced programmers (who know Python)
performance faster than anything below, though the difference will be minimal
Secondly, in my view still pythonic (often seen in codewars.com):
def get_val(self):
return True if x else False
But I guess, the style guides will recommend:
def get_val(self):
if x:
return True
else:
return False
Your second example might not be very pythonic ...
In codewars.com I have also often seen:
def get_val(x):
if x == "": return yz
elif x == "B": return xyz
else: return z
which might be a kind of switch/case imitation in Python, though
I don't think that style guides will support this ...
For the example you've given (which is minimal and complete, as it should be), both options are clear and easy to understand.
The easiest (and most pythonic) way would be:
def get_val(self):
return bool(x)
Let me comment on a more general case. Quoting from import this:
In the face of ambiguity, refuse the temptation to guess.
While the second option might appear more elegant, it is also ambiguous: Are you returning False because x is False, or because none of the previous return statements have been triggered?
Thinking of a slightly more complex example, I'd suggest to use the final return statement to give you e.g. some default option, a warning, or a ValueError.
def myfunc(x, mode):
if mode == 'square':
return x**2
if mode == 'cube':
return x**3
return x

If and Inline if, what are the advantages and disadvantages?

I'm a little curious about the difference between if and inline if, in Python. Which one is better?
Is there any reason to use inline if, other than the fact that it's shorter?
Also, is there anything wrong with this statement? I'm getting a syntax error: SyntaxError: can't assign to conditional expression
a = a*2 if b == 2 else a = a/w
The advantage of the inline if expression is that it's an expression, which means you can use it inside other expressions—list comprehensions, lambda functions, etc.
The disadvantage of the inline if expression is also that it's an expression, which means you can't use any statements inside of it.
A perfect example of the disadvantage is exactly what's causing your error: a = a/w is a statement, so you can't use it inside an expression. You have to write this:
if b == 2:
a = a*2
else:
a = a/w
Except that in this particular case, you just want to assign something to a in either case, so you can just write this:
a = a*2 if b==2 else a/w
As for the advantage, consider this:
odd_numbers = [number if number%2 else number+1 for number in numbers]
Without the if expression, you'd have to wrap the conditional in a named function—which is a good thing for non-trivial cases, but overly verbose here:
def oddify(number):
if number%2:
return number
else:
return number+1
odd_numbers = [oddify(number) for number in numbers]
Also, note that the following example is not using an if (ternary conditional) expression, but an if (conditional filter) clause:
odd_numbers = [number for number in numbers if number % 2]
The correct way to use the conditional expression is:
result = X if C else Y
what you have is:
result = X if C else result = Y
So, you should remove the result = part from there. The major advantage of conditional expression is that, it's an expression. You can use them wherever you would use a normal expression, as RHS of assignment expression, as method/function arguments, in lambdas, in list comprehension, so on. However, you can't just put any arbitrary statements in them, like say print statements.
Fo e.g. suppose you want all even integers from a list, but for all odd numbers, you want the values as 0. You would use it in list comprehension like this:
result = [x if x % 2 == 0 else 0 for x in li]
Inline if is an expression, so you can not put assignments inside.
Correct syntax would be:
a = a*2 if b == 2 else a/w
As for the usefulness, it's a question of style, and perhaps it would be a good question for Programmers StackExchange.

logical operators replacing if statements

Are the following uses of logical expressions Pythonic / pep8 compliant?
This:
x = a or b
instead of:
if not a:
x = b
else:
x = a
This:
x = a and b
instead of:
if not a:
x = a
else:
x = b
(The curve-ball?) This:
x = x or y
instead of:
if not x:
x = y
PEP 8 has nothing to do with the way you use your logical operators.
Assuming the motivation for using the logic operators instead of the conditionals is brevity, then it is better accomplished with the ternary operator:
x = a if a else b instead of x = a or b
x = b if a else a instead of x = a and b
x = x if x else y or just if not x: x = y instead of x = x or y
But nobody forbids you to use the other versions too. It's all a meter of personal opinion. The motivation behind introduction of the ternary operator was to avoid the error-prone attempts to achieve the same effect using the and and or operators (see PEP 308). They also enable fancy stuff in list comprehensions, and a few more things.
They are not introduced to replace complex if statements, but as a pythonic ternary operator: x if condition else y.
Unlike C and Java, Python's logical operators don't return booleans. I can't imagine another use case for that language feature besides the one in your question, so unless the language designers are adding features thoughtlessly, it's Pythonic (except for #3).
There are many cases where the short-circuiting logical OR can be used to your advantage. Here's a simple one from the source code of Requests:
cookies = request.cookies or {}
It's immediately obvious what the outcome of this code should be, as it reads like a sentence. Now that's not to say that the verbose versions aren't readable:
cookies = request.cookies if request.cookies else {}
And:
cookies = {}
if request.cookies:
cookies = request.cookies
But they're redundant. Python uses the same syntax for dictionaries to prevent the same sort of redundancy:
d.get('key', 'fallback')
I don't think pep8 covers this, but to me, in your examples, the if statements look more readable (especially to people new to Python (and therefore more Pythonic due to "readability counts") than logical operators, which still look more Pythonic than ternary operators.
However, ternary operators are definitely better than condition and true_value or false_value, (false_value, true_value)[condition] etc. due to being more readable and less likely to break.

How do "and" and "or" work when combined in one statement? [duplicate]

This question already has answers here:
Does Python have a ternary conditional operator?
(31 answers)
How do "and" and "or" act with non-boolean values?
(8 answers)
Closed 4 months ago.
For some reason this function confused me:
def protocol(port):
return port == "443" and "https://" or "http://"
Can somebody explain the order of what's happening behind the scenes to make this work the way it does.
I understood it as this until I tried it:
Either A)
def protocol(port):
if port == "443":
if bool("https://"):
return True
elif bool("http://"):
return True
return False
Or B)
def protocol(port):
if port == "443":
return True + "https://"
else:
return True + "http://"
Is this some sort of special case in Python, or am I completely misunderstanding how statements work?
It's an old-ish idiom; inserting parentheses to show priority,
(port == "443" and "https://") or "http://"
x and y returns y if x is truish, x if x is falsish; a or b, vice versa, returns a if it's truish, otherwise b.
So if port == "443" is true, this returns the RHS of the and, i.e., "https://". Otherwise, the and is false, so the or gets into play and returns `"http://", its RHS.
In modern Python, a better way to do translate this old-ish idiom is:
"https://" if port == "443" else "http://"
and returns the right operand if the left is true. or returns the right operand if the left is false. Otherwise they both return the left operand. They are said to coalesce.
C and X or Y is the long-running early attempt by Python users to proxy for C ? X : Y
For the most part it works, except if X is False -- this has led to many bugs in Python code, so in the Python FAQ, you'll find the more correct solution being (C and [X] or [Y])[0] because a list with a single element, regardless of its evaluated Boolean value, is always True! For example: [None] is True but None isn't. The OP's example above works because the string representing X is not empty.
However, all this changed in Python 2.5, when the ternary or conditional operator was added to the language, allowing you to use the cleaner X if C else Y as stated in other posts here. If you see code using the older format, it's because the user has been a long time Python programmer who hasn't adopted the new syntax yet, they cut-n-paste other old code, or their employer is still using 2.4.x (or earlier releases), etc.
This is an ugly hack that is not recommended. It works because of the short-circuiting behaviour of and and or and that they return the one of their arguments rather than a boolean value. Using this technique gives a risk of introducing hard-to-find bugs, so don't use it in new code.
Here's an example of how the and/or idiom can give an unexpected result:
>>> foo = 'foobar'
>>> bar = 'foobar'
>>> x = 0
>>> y = 1
>>> (foo == bar) and x or y # Will this return the value of x if (foo == bar)?
1
Prefer instead the newer notation:
return "https://" if port == "443" else "http://"
You may want to read up on the "and / or trick" of Python in this article The Peculiar Nature of And and Or in Python. It's a bit like the IIF() in VBA or VB, or ?: in C-style languages.
This construction works because it 'unfolds' to the following code:
a and b -->
if a:
return b
else:
return a
a or b -->
if a:
return a
else:
return b
With all the good answers, I found these statements help me remember this better and fit how my brain works (and hopefully for for some more out there) :
“and" returns the first False item (e.g., None, “”, [], (), {}, 0) or the last item if none (e.g. no False found)
“or" returns the first True item or the last item (e.g. no True found)**
In summary:
they all return the first item that decides the outcome of the statement. (In the worst case, the last item in the sequence)
Note this rule also applies to a chained all "and" or all "or" statement

How does the Python conditional operator workaround work?

From what I have read, I found that a built-in ternary operator does not exist (I will be happy to know more about it.).
I found the following code as a substitute:
def val():
var = float(raw_input("Age:"))
status = ("Working","Retired")[var>65]
print "You should be:",status
I couldn't understand how this code works; can anyone explain me how actually the code is working? I am also interested to know why the ternary operator doesn't exist; any references or links about this will be ore useful.
I'm running Python 2.6.4 on Windows Vista.
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 that 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.
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'
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"
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.
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.
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.
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.
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".
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.
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"))
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).
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.

Categories

Resources