6
def func():
    print 'no early termination'
    return 0

if __name__ == "__main__":
    if 1 or func():
        print 'finished'

The output:

finished

since the "1 or func()" terminates early without calling the func() because "1 or something" is always true. However, when switching to bitwise operator:

def func():
    print 'no early termination'
    return 0

if __name__ == "__main__":
    if 1 | func():
        print 'finished'

I get the output:

no early termination
finished

Why is that? this doesn't seem very efficient

zvisofer
  • 1,346
  • 18
  • 41
  • 4
    Because there is no meaningful way to short-circuit a bitwise operator, in general. And thus, that's how the behaviour of the language is defined. – Oliver Charlesworth Jan 28 '14 at 23:08
  • 2
    "bitwise or" and "logical or" sound similar, and have similar-looking operators (in some languages), but they are very different operations. – roippi Jan 28 '14 at 23:11
  • the result of bitwise operation is arithmetical and not boolean. – volcano Jan 28 '14 at 23:14
  • Short-circuiting is not always more efficient, as it amounts to a conditional jump. Jumps may cause flushing of the pipeline, which is quite costly. – starblue Jan 29 '14 at 12:51

4 Answers4

8

| can't short-circuit because its value depends on its right-hand operand even if its left-hand operand is true. For example, in

x = 1 | 2

the value of x can't be determined without knowing that there's a 2 on the right.

If we only cared about whether the if branch was taken, Python might be able to analyze the structure of the program and optimize away the func call, but the side-effects of func must happen for the program to be correct. Python can't tell whether it matters to you that 'no early termination' is printed; for all it knows, that's the signal that makes sure the dead man's switch won't release the neurotoxin.

(It's fine that the side-effects of func don't occur with or because or is specifically designed to do that. Using or tells the program you don't want the right-hand side evaluated if the left side is true.)

user2357112
  • 260,549
  • 28
  • 431
  • 505
  • 2
    True, but we CAN know from `1 | x` that for any value `x`, `1 | x` will be truthy. OP's question has merit. – Adam Smith Jan 28 '14 at 23:12
  • @zvisofer: See expanded answer. – user2357112 Jan 28 '14 at 23:14
  • @user2357112 That doesn't really make sense, since the same problem arises in `1 or func(x)` but Python short-circuits that and doesn't run `func(x)` – Adam Smith Jan 28 '14 at 23:15
  • @zvisofer That's incorrect. In the case of `bool(1 | something)`, the expression `1 | something` *is* evaluated first - and then the *result* is passed to `bool`. Python does no special static analysis here, which is what would be required to "magically ignore" the second operand to `|`. Besides, it would be terribly confusing if the behavior of: `x = 1 | f(); bool(x)` and `bool(1 | f())` differed. – user2864740 Jan 28 '14 at 23:16
  • @adsmith: That's part of the defined behavior of `or`. `func` must *not* be evaluated in that case; evaluating it would also be broken behavior. `or` is specifically designed to work that way. – user2357112 Jan 28 '14 at 23:17
  • @volcano Logical OR is *not* an arithmetic operation. – user2864740 Jan 28 '14 at 23:21
  • @volcano do you mean bitwise or? Logical or is certainly not. – Adam Smith Jan 28 '14 at 23:21
  • 2
    @adsmith: it's a bit of a tangent, but you're wrong that you can know from `1 | x` that `bool(1 | x)` will be true. `x` could have overridden `|`, after all, so you'd have to recommend that `|` would short-circuit with some built-in types but not in all cases, which means you have to at least look at the type of the second object anyway. – DSM Jan 28 '14 at 23:22
  • 2
    Yep. And even if `bool(1 | x)` were always `True`, it is [Least Astonishing](http://en.wikipedia.org/wiki/Principle_of_least_astonishment) if `|` never short circuits. A world in which it sometimes does, *just* in a boolean context, is a strange confusing world. – roippi Jan 28 '14 at 23:30
  • what about 0 & X ? why can't this be determined? – zvisofer Jan 28 '14 at 23:35
  • @zvisofer, should it also shortcircuit on 0*X? – volcano Jan 28 '14 at 23:39
  • @zvisofer: If `X` is a stand-in for something that might have side effects, the side effects must happen. Even if it's just a variable, the object might implement `__rand__` in a way that doesn't match how integers do it, so `0 & X` might evaluate to `'fish'` or feed the homeless. Some Python implementations might detect that `X` is always an integer and optimize the operation away; the standard implementation won't. – user2357112 Jan 28 '14 at 23:39
  • Incidentally, I never noticed before how the special method naming conventions make `__rand__` a rather misleading name. `Y.__rand__(X)` is called to implement `X & Y` if `X` doesn't know how to do the operation; it doesn't have anything to do with randomness. – user2357112 Jan 28 '14 at 23:41
1

These are treated more like mathematical operations rather than logical operations. That's why the entire expression needs to be evaluated.

It's similar to saying

if (1 + 3)

It can just as easily be

if (1 - 3)

with logical operations.

Dan Goldin
  • 957
  • 13
  • 32
1

With the logical operators, each side of the expression is a boolean that can be separately evaluated. Even if a particular language did not support short-circuiting, such an expression could be rewritten without the operator to achieve the same branching behavior. For example:

if 1 or func():
    print 'finished'

could be rewritten:

if 1:
    pass
elif func():
    print 'finished'

In contrast, each side of the bitwise operator is an int, as is the result, which is then implicitly cast to boolean before being evaluated by the conditional. There is only one boolean expression, and thus nothing to short-circuit.

4bar
  • 531
  • 2
  • 14
1

You're not able to short-circuit the bitwise operations since they are not returning a simple truth value. Consider that 3 or 4 is 3 while 3|4 is 7. Without fully evaluating the expression, you will not be able to get the correct bitwise result.

John Percival Hackworth
  • 11,395
  • 2
  • 29
  • 38