This issue tracker has been migrated to GitHub, and is currently read-only.
For more information, see the GitHub FAQs in the Python's Developer Guide.

classification
Title: Operator issue with "in" on same level and preceding ==
Type: behavior Stage: resolved
Components: Interpreter Core Versions: Python 3.4, Python 3.5, Python 2.7
process
Status: closed Resolution: not a bug
Dependencies: Superseder:
Assigned To: Nosy List: Sapphire Becker, ezio.melotti
Priority: normal Keywords:

Created on 2015-10-26 16:08 by Sapphire Becker, last changed 2022-04-11 14:58 by admin. This issue is now closed.

Messages (3)
msg253478 - (view) Author: Sapphire Becker (Sapphire Becker) Date: 2015-10-26 16:08
So I found this while using == as xnor, here's a summary of the issue:

>>> "h" in ["h"] == True
False
>>> ("h" in ["h"]) == True
True
>>> "h" in (["h"] == True)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: argument of type 'bool' is not iterable

Now obviously #3 should error but I would expect #1 and #2 to be the same. I checked it out with dis (in Py 3.4) and it seems like a bug. In detail:

#2's is very clean, exactly what I would expect:
>>> dis.dis('("h" in ["h"]) == True')
  1           0 LOAD_CONST               0 ('h')
              3 LOAD_CONST               2 (('h',))
              6 COMPARE_OP               6 (in)
              9 LOAD_CONST               1 (True)
             12 COMPARE_OP               2 (==)
             15 RETURN_VALUE

#1's, however, looks like a mess:
>>> dis.dis('"h" in ["h"] == True')
  1           0 LOAD_CONST               0 ('h')
              3 LOAD_CONST               0 ('h')
              6 BUILD_LIST               1
              9 DUP_TOP
             10 ROT_THREE
             11 COMPARE_OP               6 (in)
             14 JUMP_IF_FALSE_OR_POP    24
             17 LOAD_CONST               1 (True)
             20 COMPARE_OP               2 (==)
             23 RETURN_VALUE
        >>   24 ROT_TWO
             25 POP_TOP
             26 RETURN_VALUE

I had to write out the stack to understand this:
1. ['h']
2. ['h', 'h']
3. ['h', ['h']]
4. ['h', ['h'], ['h']]
5. [['h'], 'h', ['h']]
6. [['h'], True] # True = result of in operation
7. [['h']]
8. [['h'], True] # True = =='s right-hand value
9. [False] # False = result of == operation

Therefore the way to actually get true in the first case is:

>>> "h" in ["h"] == ["h"]
True


I won't pretend to know how simple it is in Python's architecture but I imagine it's just setting a higher precedence for "in" than ==?

Not a huge deal, it's workaroundable, just awkward that there wasn't really any indication it was failing so it took a bit to debug.
msg253479 - (view) Author: Ezio Melotti (ezio.melotti) * (Python committer) Date: 2015-10-26 16:50
As far as I understand, this is treated as "x < y < z" or "x == y == z":

>>> import ast
>>> ast.dump(ast.parse("1 < 2 < 3"))
'Module(body=[Expr(value=Compare(left=Num(n=1), ops=[Lt(), Lt()], comparators=[Num(n=2), Num(n=3)]))])
>>> ast.dump(ast.parse("1 == 2 == 3"))
'Module(body=[Expr(value=Compare(left=Num(n=1), ops=[Eq(), Eq()], comparators=[Num(n=2), Num(n=3)]))])'
>>> ast.dump(ast.parse("1 in 2 == 3"))
'Module(body=[Expr(value=Compare(left=Num(n=1), ops=[In(), Eq()], comparators=[Num(n=2), Num(n=3)]))])'

The following code is also valid and works as expected:
>>> 1 in [1] in [[1]]
True
>>> 2 not in [1] in [[1]]
True

Your example yields an unexpected result, but I'm not sure if and how it can be fixed.
msg253482 - (view) Author: Ezio Melotti (ezio.melotti) * (Python committer) Date: 2015-10-26 17:15
https://docs.python.org/3/reference/expressions.html#comparisons says that "a op1 b op2 c ... y opN z is equivalent to a op1 b and b op2 c and ... y opN z" and this explains the "unexpected" result:

>>> 1 in [1] == True
False
>>> 1 in [1] and [1] == True
False

"in" and "not in" are also listed among the comparison operators, so I think this behavior is expected and by design.  I don't think there's anything that needs to be fixed here.
History
Date User Action Args
2022-04-11 14:58:23adminsetgithub: 69670
2015-10-26 17:15:31ezio.melottisetstatus: open -> closed
resolution: not a bug
messages: + msg253482

stage: resolved
2015-10-26 16:50:28ezio.melottisetmessages: + msg253479
2015-10-26 16:39:47ezio.melottisetnosy: + ezio.melotti

versions: + Python 3.5
2015-10-26 16:08:58Sapphire Beckercreate