What is the motivation for the "or" operator to not return a bool?

38,776

Solution 1

It sounds like you're combining two issues into one.

First, there's the issue of short-circuiting. Marcin's answer addresses this issue perfectly, so I won't try to do any better.

Second, there's or and and returning the last-evaluated value, rather than converting it to bool. There are arguments to be made both ways, and you can find many languages on either side of the divide.

Returning the last-evaluated value allows the functionCall(x) or defaultValue shortcut, avoids a possibly wasteful conversion (why convert an int 2 into a bool 1 if the only thing you're going to do with it is check whether it's non-zero?), and is generally easier to explain. So, for various combinations of these reasons, languages like C, Lisp, Javascript, Lua, Perl, Ruby, and VB all do things this way, and so does Python.

Always returning a boolean value from an operator helps to catch some errors (especially in languages where the logical operators and the bitwise operators are easy to confuse), and it allows you to design a language where boolean checks are strictly-typed checks for true instead of just checks for nonzero, it makes the type of the operator easier to write out, and it avoids having to deal with conversion for cases where the two operands are different types (see the ?: operator in C-family languages). So, for various combinations of these reasons, languages like C++, Fortran, Smalltalk, and Haskell all do things this way.


In your question (if I understand it correctly), you're using this feature to be able to write something like:

if (x or 0) < 1:

When x could easily be None. This particular use case isn't very useful, both because the more-explicit x if x else 0 (in Python 2.5 and later) is just as easy to write and probably easier to understand (at least Guido thinks so), but also because None < 1 is the same as 0 < 1 anyway (at least in Python 2.x, so you've always got at least one of the two options)… But there are similar examples where it is useful. Compare these two:

return launchMissiles() or -1

return launchMissiles() if launchMissiles() else -1

The second one will waste a lot of missiles blowing up your enemies in Antarctica twice instead of once.


If you're curious why Python does it this way:

Back in the 1.x days, there was no bool type. You've got falsy values like None, 0, [], (), "", etc., and everything else is true, so who needs explicit False and True? Returning 1 from or would have been silly, because 1 is no more true than [1, 2, 3] or "dsfsdf". By the time bool was added (gradually over two 2.x versions, IIRC), the current logic was already solidly embedded in the language, and changing would have broken a lot of code.

So, why didn't they change it in 3.0? Many Python users, including BDFL Guido, would suggest that you shouldn't use or in this case (at the very least because it's a violation of "TOOWTDI"); you should instead store the result of the expression in a variable, e.g.:

missiles = launchMissiles()
return missiles if missiles else -1

And in fact, Guido has stated that he'd like to ban launchMissiles() or -1, and that's part of the reason he eventually accepted the ternary if-else expression that he'd rejected many times before. But many others disagree, and Guido is a benevolent DFL. Also, making or work the way you'd expect everywhere else, while refusing to do what you want (but Guido doesn't want you to want) here, would actually be pretty complicated.


So, Python will probably always be on the same side as C, Perl, and Lisp here, instead of the same side as Java, Smalltalk, and Haskell.

Solution 2

No language that i know lets you do this. So, why Python do?

Then you don't know many languages. I can't think of one language that I do know that does not exhibit this "shortcircuiting" behaviour.

It does it because it is useful to say:

a = b or K

such that a either becomes b, if b is not None (or otherwise falsy), and if not it gets the default value K.

Solution 3

Actually a number of languages do. See Wikipedia about Short-Circuit Evaluation

For the reason why short-circuit evaluation exists, wikipedia writes:

If both expressions used as conditions are simple boolean variables, it can be actually faster to evaluate both conditions used in boolean operation at once, as it always requires a single calculation cycle, as opposed to one or two cycles used in short-circuit evaluation (depending on the value of the first).

Solution 4

This behavior is not surprising, and it's quite straightforward if you consider Python has the following features regarding or, and and not logical operators:

  • Short-circuit evaluation: it only evaluates operands up to where it needs to.
  • Non-coercing result: the result is one of the operands, not coerced to bool.

And, additionally:

  • The Truth Value of an object is False only for None, False, 0, "", [], {}. Everything else has a truth value of True (this is a simplification; the correct definition is in the official docs)

Combine those features, and it leads to:

  • or : if the first operand evaluates as True, short-circuit there and return it. Or return the 2nd operand.
  • and: if the first operand evaluates as False, short-circuit there and return it. Or return the 2nd operand.

It's easier to understand if you generalize to a chain of operations:

>>> a or b or c or d

>>> a and b and c and d

Here is the "rule of thumb" I've memorized to help me easily predict the result:

  • or : returns the first "truthy" operand it finds, or the last one.
  • and: returns the first "falsy" operand it finds, or the last one.

As for your question, on why python behaves like that, well... I think because it has some very neat uses, and it's quite intuitive to understand. A common use is a series of fallback choices, the first "found" (ie, non-falsy) is used. Think about this silly example:

drink = getColdBeer() or pickNiceWine() or random.anySoda or "meh, water :/"

Or this real-world scenario:

username = cmdlineargs.username or configFile['username'] or DEFAULT_USERNAME

Which is much more concise and elegant than the alternative.

As many other answers have pointed out, Python is not alone and many other languages have the same behavior, for both short-circuit (I believe most current languanges are) and non-coercion.

Solution 5

"No language that i know lets you do this. So, why Python do?" You seem to assume that all languages should be the same. Wouldn't you expect innovation in programming languages to produce unique features that people value?

You've just pointed out why it's useful, so why wouldn't Python do it? Perhaps you should ask why other languages don't.

Share:
38,776
mclafee
Author by

mclafee

Updated on July 09, 2022

Comments

  • mclafee
    mclafee almost 2 years

    First, the code:

    >>> False or 'hello'
    'hello'
    

    This surprising behavior lets you check if x is not None and check the value of x in one line:

    >>> x = 10 if randint(0,2) == 1 else None
    >>> (x or 0) > 0
    # depend on x value...
    

    Explanation: or functions like this:

    if x is false, then y, else x

    No language that I know lets you do this. So, why does Python?