The id
function returns a unique identifier for a live object. Two objects which are live at the same time cannot have the same id. Although, the doc also mentions that this does not apply for objects which are not alive at the same time.
Two objects with non-overlapping lifetimes may have the same id()
value.
That is what happens in you case; when the second list is created the first one is no longer referenced and had been de-allocated.
We can observe this by disassembling your first example.
>>> import dis
>>> def f():
... id([1, 2, 3]) == id([1, 2, 3])
...
>>> dis.dis(f)
2 0 LOAD_GLOBAL 0 (id)
2 LOAD_CONST 1 (1)
4 LOAD_CONST 2 (2)
6 LOAD_CONST 3 (3)
8 BUILD_LIST 3 # Creation of the first list
10 CALL_FUNCTION 1 # After that point the first list is no longer referenced
12 LOAD_GLOBAL 0 (id)
14 LOAD_CONST 1 (1)
16 LOAD_CONST 2 (2)
18 LOAD_CONST 3 (3)
20 BUILD_LIST 3 # Only here is the second list allocated
22 CALL_FUNCTION 1
24 COMPARE_OP 2 (==)
26 POP_TOP
28 LOAD_CONST 0 (None)
30 RETURN_VALUE
Note that in the case of cPython, id
returns the position of an object in memory. Thus, what you observe is simply Python reusing the same memory slot for two objects with disjoint lifespans. You can reproduce the example with lists which are not equal at all.
>>> id([1, 2, 3]) == id([4, 5, 6])
True