What you see in all 3 instances is a consequence of the grammar specification of the language, and how tokens encountered in the source code are parsed to generate the parse tree.
Taking a look at this low level code should help you understand what happens under the hood. We can take these python statements, convert them into byte code and then decompile them using the dis
module:
Case 1: (0, 0) == 0, 0
>>> dis.dis(compile("(0, 0) == 0, 0", '', 'exec'))
1 0 LOAD_CONST 2 ((0, 0))
3 LOAD_CONST 0 (0)
6 COMPARE_OP 2 (==)
9 LOAD_CONST 0 (0)
12 BUILD_TUPLE 2
15 POP_TOP
16 LOAD_CONST 1 (None)
19 RETURN_VALUE
(0, 0)
is first compared to 0
first and evaluated to False
. A tuple is then constructed with this result and last 0
, so you get (False, 0)
.
Case 2: 0, 0 == (0, 0)
>>> dis.dis(compile("0, 0 == (0, 0)", '', 'exec'))
1 0 LOAD_CONST 0 (0)
3 LOAD_CONST 0 (0)
6 LOAD_CONST 2 ((0, 0))
9 COMPARE_OP 2 (==)
12 BUILD_TUPLE 2
15 POP_TOP
16 LOAD_CONST 1 (None)
19 RETURN_VALUE
A tuple is constructed with 0
as the first element. For the second element, the same check is done as in the first case and evaluated to False
, so you get (0, False)
.
Case 3: (0, 0) == (0, 0)
>>> dis.dis(compile("(0, 0) == (0, 0)", '', 'exec'))
1 0 LOAD_CONST 2 ((0, 0))
3 LOAD_CONST 3 ((0, 0))
6 COMPARE_OP 2 (==)
9 POP_TOP
10 LOAD_CONST 1 (None)
13 RETURN_VALUE
Here, as you see, you're just comparing those two (0, 0)
tuples and returning True
.