4 ms·
I think this one is good: False == False in [False] # Outputs True It doesn't make sense no matter which way you evaluate it. Equality first: False ==
by craftkiller 1y ago
I think this one is good:
False == False in [False]
# Outputs True
It doesn't make sense no matter which way you evaluate it. Equality first:
False == False # True
True in [False] # False
or `in` first:
False in [False] # True
False == True # False
- ltbarcly3 1y agoOk I'll admit this is a real WTF
- Upvoter33 1y agochained comparisons are weird and counter-intuitive, sometimes
- quuxplusone 1y agoPython is my second language (after C++) and for me the surprising thing here is not "chained comparisons are weird" but rather "`in` is a comparison operator." So for example `1 in [1,2] in [[1,2],[3,4]]` is True... but off the top of my head I don't see any legitimate use-case for that facility. Intuitively, I'd think a "comparison operator" should be homogeneous — should take the same type on LHS and RHS. So, like, "is-subset-of" could sanely be a comparison operator, but `in` can't be.
- zahlman 1y ago>and for me the surprising thing here is not "chained comparisons are weird" but rather "`in` is a comparison operator." Python documentation calls it a "comparison operator" (https://docs.python.org/3/reference/expressions.html#comparisons https://docs.python.org/3/reference/expressions.html#compari...) but a broader, better term is "relational operator". It expresses a possible relationship between two arguments, and evaluates whether those arguments have that relationship. In this case, "containment" (which intuitively encompasses set membership and substrings). A finer distinction could have been made, arguably, but it's far too late to change now. (And everyone is traumatized by the 2->3 transition.)
- quuxplusone 1y agoAh, `in` for strings satisfies my intuition re homogeneity. So I guess that makes sense enough (although if I ran the zoo I wouldn't have done it!). Thanks!
- None4U 1y agoYeah, having the expansion rule operate on operators that are clearly not transitive serves no purpose (1 < 2 < 3 is at least a math notation)
- daedrdev 1y agoIts apparently trying to allow x < y < z be a legal statement, meaning that we get: False == False and False in [False] is a expansion of that statement, just like x < y and y < z is the expansion of x < y < z
- jnpnj 1y agoOh yeah, that's a great one, there must be a compounded boolean expression logic escaping us I'll add: >>> False == False in [True] False >>> True == True in [True] True ps: some ast poking >>> ast.show(ast.parse("False == False in [False]")) Module( body=[ Expr( value=Compare( left=Constant(value=False), ops=[ Eq(), In()], comparators=[ Constant(value=False), List( elts=[ Constant(value=False)], ctx=Load())]))]) >>> ast.show(ast.parse("(False == False) in [False]")) Module( body=[ Expr( value=Compare( left=Compare( left=Constant(value=False), ops=[ Eq()], comparators=[ Constant(value=False)]), ops=[ In()], comparators=[ List( elts=[ Constant(value=False)], ctx=Load())]))]) note the first tree as one Comparator node with multiple ops, time to read the spec to know how these are evaluated
- Jtsummers 1y agoThe document explains it. When dealing with these comparators, when chained they get separated and you have the equivalent of this: False == False in [True] => (False == False) and (False in [True]) If you put it into a function like this: def foo(): return False == False in [True] And then disassemble it you'll get this byte code (3.13, may vary by version): RESUME 0 LOAD_CONST 1 (False) LOAD_CONST 1 (False) SWAP 2 COPY 2 COMPARE_OP 72 (==) COPY 1 TO_BOOL POP_JUMP_IF_FALSE 5 (to L1) POP_TOP LOAD_CONST 2 ((True,)) CONTAINS_OP 0 RETURN_VALUE L1: SWAP 2 POP_TOP RETURN_VALUE There is no literal `and` operation occurring here, instead it first does the `False == False`, then jumps if false or continues to the second comparison. For the literals we've given it, it'll go on to the second comparison which will result in false since `False` is not in `(True,)`. EDIT: And here's the non-chained version disassembled: def bar(): return (False == False) in [True] RESUME 0 LOAD_CONST 1 (False) LOAD_CONST 1 (False) COMPARE_OP 72 (==) LOAD_CONST 2 ((False,)) CONTAINS_OP 0 RETURN_VALUE
- 1y ago
- its-summertime 1y agoreturn False == False in [False] is roughly a = False b = False c = [False] (a == b) and (b in c) is less roughly a = False b = False if not (a == b): # short circuits https://news.ycombinator.com/item?id=44619401 return False c = [False] if not (b in c): return False return True
- zahlman 1y ago> It doesn't make sense no matter which way you evaluate it It makes sense when you evaluate it the way it's intended to be understood (https://stackoverflow.com/questions/6074018 https://stackoverflow.com/questions/6074018). There are other languages that do this, and it was consciously designed to work this way. The idea that you should have to evaluate one of the operators "first" may make sense to someone trying to implement a parser with techniques from the 70s, but for languages like Python, the design is clearly more about what makes sense for the code author. Arguably it's surprising that `in` is treated as on par with `==`. But on the other hand, even someone trying to implement a parser with techniques from the 70s would have to admit that both are "relational operators".