0

If I have this Python code:

if a() or b():
   do_something()

Will a() always execute before b()?

I know from similar code in C, C compilers will sometimes optimize b() to execute before a(). Is this also the case with the Python interpreter?

I'm using Python 3.10 if that affects it

Brett
  • 59
  • 6
  • What "similar" C code? – Kelly Bundy Mar 10 '23 at 19:18
  • Such an optimization would not be legal in C for *logical* boolean operators (`&&`, `||`) because they are short-circuiting. Perhaps you are confusing them with *bitwise* operators (`&`, `|`). – jamesdlin Mar 10 '23 at 19:20
  • I had some c code a while back that looked something like: ```while(a() || b()) { do_something(); }``` and it got optimized into b() || a(). It was extremely frustrating to figure that out. I def had some optimization flags going which may have affected it. @jamesdlin – Brett Mar 10 '23 at 19:54
  • @Brett If that happened then your C compiler was broken and non-conformant. – jamesdlin Mar 10 '23 at 20:25
  • @jamesdlin Aren't compilers allowed to do such things if it makes no observable difference? – Kelly Bundy Mar 10 '23 at 20:37
  • @Brett Not in this case. `&&` and `||` are *required* to create a sequence point that guarantees that the left operand is evaluated first. There are many other types of expressions where evaluation order is unspecified and thus implementation-dependent. – jamesdlin Mar 10 '23 at 20:50
  • Also see: https://stackoverflow.com/q/628526/ – jamesdlin Mar 10 '23 at 20:53
  • @jamesdlin So is for example [this answer](https://stackoverflow.com/a/38061717/12671057) wrong, or do I misunderstand it? – Kelly Bundy Mar 10 '23 at 21:03
  • @KellyBundy Well, the point is that the compiler must generate code that at a macroscopic level behaves as-if the left operand is evaluated first. At a microscopic level, ok, the compiler can reorder *if* there are no observable differences, but if there are no observable differences, you can't tell which was evaluated first anyway. – jamesdlin Mar 10 '23 at 22:37
  • @jamesdlin Yes, I'm wondering how Brett was able to tell, and why that was a problem. – Kelly Bundy Mar 10 '23 at 23:55
  • @jamesdlin I was using Microchip's MPLAB XC8 compiler for the code. I was getting some unexplainable results in my code that were obviously wrong. I tried everything I could think of but was still stumped. Then I tried splitting the while loop from ```while(a() || b()) { do_something(); }``` to ```while(1) { if (a()) {do_something();} else if (b()) {do_something();} else {break;}}``` and it worked. – Brett Mar 17 '23 at 16:56

2 Answers2

2

a() will execute before b()

This is guaranteed.

In fact, b() will not execute if a() is already true-like.

A commenter was asking where the guarantee is

Source: https://docs.python.org/3/library/stdtypes.html#boolean-operations-and-or-not

enter image description here

For the short-circuiting to work, i.e. only call the second if the first is false-like, the first must be executed before the decision is made on whether to call the second. Therefore the first will be called before the second.

ProfDFrancis
  • 8,816
  • 1
  • 17
  • 26
  • @KellyBundy [*Python evaluates expressions from left to right. Notice that while evaluating an assignment, the right-hand side is evaluated before the left-hand side.*](https://docs.python.org/3/reference/expressions.html#evaluation-order) – Bakuriu Mar 10 '23 at 19:24
0

Section 6.16 of the language refers states that expressions are evaluated left to right, so yes a() will always execute before b(), doesn't matter which operand is between them:

Python evaluates expressions from left to right. Notice that while evaluating an assignment, the right-hand side is evaluated before the left-hand side.

In the following lines, expressions will be evaluated in the arithmetic order of their suffixes:

expr1, expr2, expr3, expr4
(expr1, expr2, expr3, expr4)
{expr1: expr2, expr3: expr4}
expr1 + expr2 * (expr3 - expr4)
expr1(expr2, expr3, *expr4, **expr5)
expr3, expr4 = expr1, expr2

Whether or not b() is executed at all depends on the operand type.

For boolean operands, as described by @Eureka in their answer, the answer is that b() is evaluated only if necessary to compute the result (i.e. the operand will "short circuit"). For other operands b() will be evaluated unless a() raises an exception.

Bakuriu
  • 98,325
  • 22
  • 197
  • 231
  • `print(1) if not print(2) else print(3)` prints `2` before it prints `1`. – Kelly Bundy Mar 10 '23 at 19:33
  • @KellyBundy So what? That's a **conditional** expression. Obviously you must evaluate the condition before the "body". If it printed `1` before `2` then it would not make sense as an expression. It's like saying that in `a + b` you expect the `+` to be evaluted before its operands – Bakuriu Mar 10 '23 at 19:39