Strange use of python's and / or operator
I'm trying to learn python and came across some code that is nice and short but doesn't totally make sense
the context was:
def fn(*args): return len(args) and max(args)-min(args)
I get what it's doing, but why does python do this - ie return the value rather than True/False?
10 and 7-2
returns 5. Similarly, changing the and to or will result in a change in functionality. So
10 or 7 - 2
Would return 10.
Is this legit/reliable style, or are there any gotchas on this?
TL;DR
Logical AND (
and):
Return the first Falsey value if there are any, else return the last value in the expression.
Logical OR (
or):
Return the first Truthy value if there are any, else return the last value in the expression.
return len(args) and max(args) - min(args)
Is a very
pythonic concise way of saying:
if
argsis not empty, return the result of
max(args) - min(args).
The logical
and and
or operators are not restricted to working with, or returning boolean values. Any object with a truthiness value can be tested here. This includes
int,
str,
list,
dict,
tuple,
set,
NoneType, and user defined objects. Short circuiting rules still apply as well.
Note that this is a more concise way of constructing an
if-else expression:
return exp1 and exp2
Should (roughly) translate to:
r1 = exp1 if not r1: return r1 return exp2
Here's some examples of how and where these constructs can be used to concisely handle invalid input.
Example with
and (as shown by OP)
Return the difference between the
min and
max of a group of arguments.
def foo(*args): return len(args) and max(args) - min(args) foo(1, 2, 3, 4, 5) 4 foo() 0
Since
and is used, the second expression must also be evaluated if the first is
True. Note that, if the first expression is evaluated to be Truthy, the result returned is always the result of the second expression.
If the first expression is evaluated to be Falsey, then the result returned is the result of the first expression.
If
foo receives any arguments,
len(args) is greater than
0 (a positive number), so the result returned is
max(args) - min(args).
If
foo does not receive arguments,
len(args) is
0 which is Falsey, and
0 is returned.
Note that an alternative way to write this function would be:
def foo(*args): if not len(args): return 0 return max(args) - min(args)
Example with
or
Return all numbers over
9000.
def foo(*args): return [x for x in args if x > 9000] or 'No number over 9000!' foo(9004, 1, 2, 500) [9004] foo(1, 2, 3, 4) 'No number over 9000!'
In this case
or is used. If the first expression is Truthy, then the result returned is the result of the first expression. Otherwise, both expressions are evaluated and the result returned is the result of the second expression.
foo performs a filtration on the list to retain all numbers over
9000. If there exist any such numbers, the result of the list comprehension is a non-empty list which is Truthy, so it is returned (short circuiting in action here).
If there exist no such numbers, then the result of the list comp is
[] which is Falsey. So the second expression is now evaluated (a non-empty string) and is returned.
And the alternative for this function would be:
def foo(*args): r = [x for x in args if x > 9000] if not r: return 'No number over 9000!' return r
From: stackoverflow.com/q/47007680