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.
Related
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
Code like this looks strange,
x = 1 if condition else 5
Most Programmers are accustomed to the pattern <condition> : <on-true> : <on-false>. Why does python break this well known and established pattern with its ternary operator? Why not something like this:
x = if condition then 1 else 5
Was there a reason for this design descision?
Python is more English like, so it should be read like this
one, if condition is Truthy, else 5
Read more about this in the documentation
Also, read this PEP 308, written by the BDFL himself.
Quoting from the BDFL's pronouncement mail,
After a long discussion I've decided to add a shortcut conditional
expression to Python 2.5.
The syntax will be
A if C else B
This first evaluates C; if it is true, A is evaluated to give the
result, otherwise, B is evaluated to give the result.
The priorities will be such that you can write
x = A if C else B
x = lambda: A if C else B
x = A if C else B if D else E
But you'd have to write
if (A if C else B):
[x for x in seq if (A if C else B)]
A if (X if C else Y) else B
(A if C else B) if D else E
Note that all these are intentionally ugly. :)
In general, 'if' and 'else' bind less tight than everything except
lambda.
We will adjust the syntax of what goes inside an 'if' to disallow
lambda; currently
if lambda: x:
is accepted but quite useless (it's always true) so this will be
disallowed.
Flames, pleas to reconsider, etc., to /dev/null.
Congratulations gracefully accepted.
It's still my language! :-)
The reason can be summarised as because Guido said so:
The syntax will be
A if C else B
[...]
Flames, pleas to reconsider, etc., to /dev/null.
Congratulations gracefully accepted.
It's still my language! :-)
He cut the Gordian knot that is PEP 308; each variation had their supporters and detractors, and it was his personal preference that he picked here.
Python is not C, it is Python, and it is Guido's Python.
Personally, I agree with the pick, btw. I like the syntax.
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.
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).
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