Strange use of "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 args is 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 withand (as shown by OP)

Return the difference between themin 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 withor

Return all numbers over9000.

    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