3

While looking through another question, a new asker used an expression of the form:

foo in bar == baz

I started to explain how Python interprets that expression, but quickly realized I didn't actually know how Python interprets that expression. Being too lazy to look up the order of operations, I figured it had to be equivalent to either:

(foo in bar) == baz

Or:

foo in (bar == baz)

I discovered from some testing that neither of these was actually true. Digging a little further, the bytecode for the two parenthesized statements was pretty straightforward:

>>> dis(lambda foo, bar, baz:(foo in bar) == baz)
  1           0 LOAD_FAST                0 (foo)
              3 LOAD_FAST                1 (bar)
              6 COMPARE_OP               6 (in)
              9 LOAD_FAST                2 (baz)
             12 COMPARE_OP               2 (==)
             15 RETURN_VALUE
>>> dis(lambda foo, bar, baz:foo in (bar == baz))
  1           0 LOAD_FAST                0 (foo)
              3 LOAD_FAST                1 (bar)
              6 LOAD_FAST                2 (baz)
              9 COMPARE_OP               2 (==)
             12 COMPARE_OP               6 (in)
             15 RETURN_VALUE 

But the bytecode for the unadorned version is much more interesting:

>>> dis(lambda foo, bar, baz:foo in bar == baz)
  1           0 LOAD_FAST                0 (foo)
              3 LOAD_FAST                1 (bar)
              6 DUP_TOP
              7 ROT_THREE
              8 COMPARE_OP               6 (in)
             11 JUMP_IF_FALSE_OR_POP    21
             14 LOAD_FAST                2 (baz)
             17 COMPARE_OP               2 (==)
             20 RETURN_VALUE
        >>   21 ROT_TWO
             22 POP_TOP
             23 RETURN_VALUE

From my rudimentary grasp of bytecode, this appears to be equivalent to:

if foo in bar:
    return bar == baz
else:
    return False

I'm completely at a loss as to what the purpose of this construction is. Have I stumbled across some esoteric shorthand? Is this a side effect of some other syntax?

glibdud
  • 7,550
  • 4
  • 27
  • 37
  • Chained comparisons are a language feature. – Martijn Pieters Apr 24 '17 at 19:59
  • @MartijnPieters Oh, so this is the same sort of thing as `foo < bar < baz`? – glibdud Apr 24 '17 at 20:00
  • It is exactly the same thing. `is` and `==` are comparison operators too. – Martijn Pieters Apr 24 '17 at 20:01
  • This in particular shows how chained operators may "short circuit" computation and skip the potentially expensive `bar == baz` if `foo in bar` is false. – Aaron Apr 24 '17 at 20:04
  • Ok, so it basically gets interpreted as `(foo in bar) and (bar == baz)`. That makes a lot more sense. – glibdud Apr 24 '17 at 20:05
  • not really, your if-else evaluation in the question body is the most accurate. – Aaron Apr 24 '17 at 20:07
  • @Aaron Well, I was applying a line from the [docs](https://docs.python.org/3/reference/expressions.html#comparisons): "Comparisons can be chained arbitrarily, e.g., `x < y <= z` is equivalent to `x < y and y <= z`, except that `y` is evaluated only once." – glibdud Apr 24 '17 at 20:09
  • Oh, yah.. I was thinking execution wise. (ie bytecode) the meaning is the same, you are correct – Aaron Apr 24 '17 at 20:19

0 Answers0