Consider this example
>>> dis.dis("[1 for i in range(3)]")
1 0 LOAD_CONST 0 (<code object <listcomp> at 0x10d6045d0, file "<dis>", line 1>)
2 LOAD_CONST 1 ('<listcomp>')
4 MAKE_FUNCTION 0
6 LOAD_NAME 0 (range)
8 LOAD_CONST 2 (3)
10 CALL_FUNCTION 1
12 GET_ITER
14 CALL_FUNCTION 1
16 RETURN_VALUE
Disassembly of <code object <listcomp> at 0x10d6045d0, file "<dis>", line 1>:
1 0 BUILD_LIST 0
2 LOAD_FAST 0 (.0) <------ Look at this 🧐🧐
>> 4 FOR_ITER 8 (to 14)
6 STORE_FAST 1 (i)
8 LOAD_CONST 0 (1)
10 LIST_APPEND 2
12 JUMP_ABSOLUTE 4
>> 14 RETURN_VALUE
There's a strange .0
in the disassembled byte code.
By analyzing each instruction, I know that it represents an iterator iter(range(3))
, but why doesn't Python use a more intuitive representation like iter(range(3))
, since it already knows its value?
That's a variable name. The generated function for the list comprehension takes a single argument, which is given the normally-forbidden name .0
to make sure the name doesn't clash with any regular variables. If Python had to generate more names, they would go .1
, .2
, etc.
The dis
output shows .0
because the LOAD_FAST
instruction is a read from the .0
variable. dis
isn't going to go looking for the expression the variable's value came from; that would be a lot of extra work, wouldn't be possible in general, and would be less true to the bytecode it's disassembling.