[] and list() and 'int' object is not iterable in Python

Solution 1:

They're entirely different things; list is a built-in and [] are operators that can be used for, for example, either list comprehension or for list initialisation (which is what you're doing), but also for accessing certain indices in containers, etc. So you're kind of comparing apples and oranges.

Between list with generator expressions and list comprehension, the latter is actually faster:

$ python3 -m timeit '[_ for _ in range(1000)]'
10000 loops, best of 5: 29.4 usec per loop
$ python3 -m timeit 'list(_ for _ in range(1000))'
5000 loops, best of 5: 42.9 usec per loop

If we disassemble into bytecode we get:

  • for list comprehension
>>> dis.dis('[_ for _ in range(10)]')
  1           0 LOAD_CONST               0 (<code object <listcomp> at 0x10d0647c0, file "<dis>", line 1>)
              2 LOAD_CONST               1 ('<listcomp>')
              4 MAKE_FUNCTION            0
              6 LOAD_NAME                0 (range)
              8 LOAD_CONST               2 (10)
             10 CALL_FUNCTION            1
             12 GET_ITER
             14 CALL_FUNCTION            1
             16 RETURN_VALUE

Disassembly of <code object <listcomp> at 0x10d0647c0, file "<dis>", line 1>:
  1           0 BUILD_LIST               0
              2 LOAD_FAST                0 (.0)
        >>    4 FOR_ITER                 8 (to 14)
              6 STORE_FAST               1 (_)
              8 LOAD_FAST                1 (_)
             10 LIST_APPEND              2
             12 JUMP_ABSOLUTE            4
        >>   14 RETURN_VALUE
  • for list plus a generator expression
>>> dis.dis('list(_ for _ in range(10))')
  1           0 LOAD_NAME                0 (list)
              2 LOAD_CONST               0 (<code object <genexpr> at 0x10d0647c0, file "<dis>", line 1>)
              4 LOAD_CONST               1 ('<genexpr>')
              6 MAKE_FUNCTION            0
              8 LOAD_NAME                1 (range)
             10 LOAD_CONST               2 (10)
             12 CALL_FUNCTION            1
             14 GET_ITER
             16 CALL_FUNCTION            1
             18 CALL_FUNCTION            1
             20 RETURN_VALUE

Disassembly of <code object <genexpr> at 0x10d0647c0, file "<dis>", line 1>:
  1           0 LOAD_FAST                0 (.0)
        >>    2 FOR_ITER                10 (to 14)
              4 STORE_FAST               1 (_)
              6 LOAD_FAST                1 (_)
              8 YIELD_VALUE
             10 POP_TOP
             12 JUMP_ABSOLUTE            2
        >>   14 LOAD_CONST               0 (None)
             16 RETURN_VALUE

So outside of having to look up the name (list, LOAD_NAME), it seems to be mostly down to internal design; that the generator expression is popping indicates a stack.


Compare this with what you were doing:

>>> dis.dis('[1]')
  1           0 LOAD_CONST               0 (1)
              2 BUILD_LIST               1
              4 RETURN_VALUE
>>> dis.dis('list(1)')
  1           0 LOAD_NAME                0 (list)
              2 LOAD_CONST               0 (1)
              4 CALL_FUNCTION            1
              6 RETURN_VALUE

First one builds a list, while the second is (after looking up the name) trying to call the function list (which actually is a mutable sequence type class).