Travis
Travis

Reputation: 1241

Python use multiple operators in a boolean expression

Background

As we all known, we'd better not assginment several variables use chain assignment like a = b = [1,2,3], because a will be a shalow copy of b. It is not safe because a will change when we revise b.

However, if the initialization is immutable, we can do like this a = b = 1 and it's safe.

Recently, I find a strange usage of multiple operators in the condition expression of a control flow, like if 1 < b < 2: or while a == b == c == 1:

For example, the following control flow excute different chunks under different conditions:

a = 1
b = 1
c = 2

if a == b == c == 1:
    print('All equal!')
else:
    print('At least one variable is not equal to others')

At least one variable is not equal to others


My question

Is this multiple operation usage safe in a boolean expression in within a control flow? I know we should check the operator precedence when we write a boolean expression. Is there anything else we should keep an eye on? I try for a while and I think the multiple operator usage is safe.


Bytecode analysis

I type the bytecode of the following program:

a = 1;b =2;c =1.5
a<b<c
import dis
dis.dis('a<b<c')
  1           0 LOAD_NAME                0 (a)
              2 LOAD_NAME                1 (b)
              4 DUP_TOP
              6 ROT_THREE
              8 COMPARE_OP               0 (<)
             10 JUMP_IF_FALSE_OR_POP    18
             12 LOAD_NAME                2 (c)
             14 COMPARE_OP               0 (<)
             16 RETURN_VALUE
>   18 ROT_TWO
             20 POP_TOP
             22 RETURN_VALUE```

I can only recognize that it compare a and b at step 10 and then compare a and c at step 14. But why it still return False. I not familiar with analysing bytecode. If someone can help with analysing it, I will be very appreciated! Here is an official guide of Module: dis

Upvotes: 1

Views: 2199

Answers (2)

Ch3steR
Ch3steR

Reputation: 20689

This is the code given by you.

a = 1
b = 1
c = 2

if a == b == c == 1:
    print('All equal!')
else:
    print('At least one variable is not equal to others')

Let's understand what it means.a==b==c==1 is evaluated to True only when all three of them are equal to 1. Else evaluated to False. a==b==c is evaluated as a==b and b==c and c==a.

To get what you wanted you have to do this.

if a==b==c==1:
    print('All are equal')
elif (a==b) or (b==c) or (c==a):
   print('At least one variable is not equal to others')
else:
    print('none of them are equal')

Now for analysis the second you provided with bytcode.

a = 1;b =2;c =1.5
a<b<c

a<b<c is evaluated as a<b and b<c in your case this is 1<2 and 2<1.5 which is evaluated to False. 1<2 is evaluated to True and 2<1.5 is evaluated to False. True andFalseis evaluated toFalse`.

Byte code:

In [2]: a=1;b=2;c=1.5

In [3]: dis.dis('a<b and b<c')
  1           0 LOAD_NAME                0 (a)
              2 LOAD_NAME                1 (b)
              4 COMPARE_OP               0 (<)
              6 JUMP_IF_FALSE_OR_POP    14
              8 LOAD_NAME                1 (b)
             10 LOAD_NAME                2 (c)
             12 COMPARE_OP               0 (<)
        >>   14 RETURN_VALUE

6 JUMP_IF_FALSE_OR_POP 14 What this line tells us is jump to line 14 if false. In logical and False and any_bool_value always evaluates to False.

Now if 6 JUMP_IF_FALSE_OR_POP 14 is True then it continues executing 8 to 14.

And it's safe to use Multiple boolean operators in a single expression.

Upvotes: 1

Sayandip Dutta
Sayandip Dutta

Reputation: 15872

From 0 to 8 it compares a < b, at 10 checks if its False, if yes go to 18, rotate stack, pop top value, that is False, because a<b<c is a<b and b<c, so if first value is False, does not need to check the second condition.

But in this case a < b == True, so it continues. At this point as the it has passed the first checkpoint (10) it knows the first condition must have been True so it returns whatever the value is from the b < c condition, which is False, so you get False.

On the contrary, if you check disassembly of 'a

  1           0 LOAD_NAME                0 (a)
              2 LOAD_NAME                1 (b)
              4 COMPARE_OP               0 (<)
              6 JUMP_IF_TRUE_OR_POP     14
              8 LOAD_NAME                1 (b)
             10 LOAD_NAME                2 (c)
             12 COMPARE_OP               0 (<)
        >>   14 RETURN_VALUE

It does the opposite, checks (6) if the first condition is True, if yes, it does not matter what the next condition evaluates to and returns the value, else, returns whatever the next condition evaluates to.

Upvotes: 1

Related Questions