5 ms·
"pretty unintuitive" is an understatement... In [1]: import dis In [2]: def a(): ...: return (1 in [1,2,3] is True) In [3]: def b():
by vanni 6y ago
"pretty unintuitive" is an understatement...
In [1]: import dis
In [2]: def a():
...: return (1 in [1,2,3] is True)
In [3]: def b():
..: return ((1 in [1,2,3]) is True)
In [4]: a()
Out[4]: False
In [5]: b()
Out[5]: True
In [6]: dis.dis(a)
2 0 LOAD_CONST 1 (1)
2 LOAD_CONST 1 (1)
4 LOAD_CONST 2 (2)
6 LOAD_CONST 3 (3)
8 BUILD_LIST 3
10 DUP_TOP
12 ROT_THREE
14 COMPARE_OP 6 (in)
16 JUMP_IF_FALSE_OR_POP 24
18 LOAD_CONST 4 (True)
20 COMPARE_OP 8 (is)
22 RETURN_VALUE
>> 24 ROT_TWO
26 POP_TOP
28 RETURN_VALUE
In [7]: dis.dis(b)
2 0 LOAD_CONST 1 (1)
2 LOAD_CONST 5 ((1, 2, 3))
4 COMPARE_OP 6 (in)
6 LOAD_CONST 4 (True)
8 COMPARE_OP 8 (is)
10 RETURN_VALUE
Why?!
- zelphirkalt 6y agoI don't see anything surprising. "is" has higher prevalenz than "in" and that's it. Or am I misreading something?
- vanni 6y agoIt's not a matter of operator precedence for two reasons: 1) "in" and "is" have same precedence, and group left to right (see https://docs.python.org/3/reference/expressions.html#operator-precedence https://docs.python.org/3/reference/expressions.html#operato...) 2) you'll have runtime error: In [1]: def c(): ...: return (1 in ([1,2,3] is True)) In [2]: c() ... TypeError: argument of type 'bool' is not iterable It seems related to CPython bytecode compiler implementation, the two functions are parsed in a different way, parentheses make the compiler go on a different path... but I'd like to understand why, without diving into CPython source code :) Anyone?
- morelisp 6y agoIf this was the reason you'd get a TypeError, since there's no `bool.__contains__`.
- vanni 6y agoThe disassembly in parent comment is made with CPython 3.6.9, but nothing substantial changes on CPython 3.8.x/3.9.x or PyPy. Maybe it is related to grammar, and not to compiler... AST dump in CPython 3.6.9 (manually formatted): In [1]: import ast In [2]: print(ast.dump(ast.parse("""\ ...: def a(): ...: return (1 in [1,2,3] is True)"""))) Module( body=[ FunctionDef( name='a', args=arguments(args=[], vararg=None, kwonlyargs=[], kw_defaults=[], kwarg=None, defaults=[]), body=[ Return( value=Compare( left=Num(n=1), ops=[In(), Is()], comparators=[ List(elts=[Num(n=1), Num(n=2), Num(n=3)], ctx=Load()), NameConstant(value=True) ] ) ) ], decorator_list=[], returns=None ) ] ) In [3]: print(ast.dump(ast.parse("""\ ...: def b(): ...: return ((1 in [1,2,3]) is True)"""))) Module( body=[ FunctionDef( name='b', args=arguments(args=[], vararg=None, kwonlyargs=[], kw_defaults=[], kwarg=None, defaults=[]), body=[ Return( value=Compare( left=Compare( left=Num(n=1), ops=[In()], comparators=[List(elts=[Num(n=1), Num(n=2), Num(n=3)], ctx=Load())] ), ops=[Is()], comparators=[NameConstant(value=True)] ) ) ], decorator_list=[], returns=None ) ] )
- vanni 6y agoOK, mystery solved. In Python: 1 in [1,2,3] is True is evaluated as (1 in [1, 2, 3]) and ([1, 2, 3] is True) similarly to 1 < 2 < 3 :facepalm: