Search code examples
pythoniteratorbytecode

Why does Python use ".0" to represent an iterator in disassembled byte code?


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?


Solution

  • 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.