Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Python: Error in nested one line for loop in static context [duplicate]

In nested one line for loop in static context the second loop does not accept variable (here 'l')

Do I have to understand that?

class T():
    l = 3
    t1 = [(k,v) for k in range(l) for v in range(3)] # ok
    t2 = [(k,v) for k in range(l) for v in range(l)] 
    #                   error 'l' is not defined ^
like image 319
Ren Tier Avatar asked Jan 23 '26 03:01

Ren Tier


1 Answers

Check bytecode with dis.dis:

>>> dis('''class T:
...     l = 3
...     t = [(i, j) for i in range(l) for j in range(l)]''')

For the list comprehension in the class, the generated bytecode is as follows:

Disassembly of <code object <listcomp> at 0x000002B84F04ED90, file "<dis>", line 3>:
  3           0 BUILD_LIST               0
              2 LOAD_FAST                0 (.0)     # load iterator of "range(l)" of the outer for loop
        >>    4 FOR_ITER                13 (to 32)
              6 STORE_FAST               1 (i)
              8 LOAD_GLOBAL              0 (range)
             10 LOAD_GLOBAL              1 (l)      # load "l" of the inner for loop
             12 CALL_FUNCTION            1
             14 GET_ITER
        >>   16 FOR_ITER                 6 (to 30)
             18 STORE_FAST               2 (j)
             20 LOAD_FAST                1 (i)
             22 LOAD_FAST                2 (j)
             24 BUILD_TUPLE              2
             26 LIST_APPEND              3
             28 JUMP_ABSOLUTE            8 (to 16)
        >>   30 JUMP_ABSOLUTE            2 (to 4)
        >>   32 RETURN_VALUE

Notice the two comments I marked, which correspond to the bytecode of load the iterator of range(l) in the outer for loop and the bytecode of load l in the inner for loop respectively. The obvious difference is that the iterator of range(l) in the outer for loop is passed into the list comprehension as a function parameter, while the inner range(l) needs to be loaded dynamically. This attempts to find them from the global namespace, because l is not in the global namespace but in the private namespace of the class T. Therefore, the list comprehension cannot find it, resulting in an error.

Some different from list comprehension in functions: A well-known thing is that the same code will not make errors in the definition body of the function. This time, check the bytecode of the function with the same definition body:

>>> dis('''def foo():
...     l = 3
...     t = [(i, j) for i in range(l) for j in range(l)]''')
Disassembly of <code object <listcomp> at 0x000002B850EF5A50, file "<dis>", line 3>:
  3           0 BUILD_LIST               0
              2 LOAD_FAST                0 (.0)     # load iterator of "range(l)" of the outer for loop
        >>    4 FOR_ITER                13 (to 32)
              6 STORE_FAST               1 (i)
              8 LOAD_GLOBAL              0 (range)
             10 LOAD_DEREF               0 (l)      # load "l" of the inner for loop
             12 CALL_FUNCTION            1
             14 GET_ITER
        >>   16 FOR_ITER                 6 (to 30)
             18 STORE_FAST               2 (j)
             20 LOAD_FAST                1 (i)
             22 LOAD_FAST                2 (j)
             24 BUILD_TUPLE              2
             26 LIST_APPEND              3
             28 JUMP_ABSOLUTE            8 (to 16)
        >>   30 JUMP_ABSOLUTE            2 (to 4)
        >>   32 RETURN_VALUE

Note that the loading method of l in the inner loop is different from that in the class definition body. Python treats l here as closure, the bytecode of the function is here:

Disassembly of <code object foo at 0x000002B850EF5FD0, file "<dis>", line 1>:
  2           0 LOAD_CONST               1 (3)
              2 STORE_DEREF              0 (l)

  3           4 LOAD_CLOSURE             0 (l)    # treats "l" as closure
              6 BUILD_TUPLE              1
              8 LOAD_CONST               2 (<code object <listcomp> at 0x000002B850EF5A50, file "<dis>", line 3>)
             10 LOAD_CONST               3 ('foo.<locals>.<listcomp>')
             12 MAKE_FUNCTION            8 (closure)
             14 LOAD_GLOBAL              0 (range)
             16 LOAD_DEREF               0 (l)
             18 CALL_FUNCTION            1
             20 GET_ITER
             22 CALL_FUNCTION            1
             24 STORE_FAST               0 (t)
             26 LOAD_CONST               0 (None)
             28 RETURN_VALUE
like image 103
Mechanic Pig Avatar answered Jan 24 '26 17:01

Mechanic Pig



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!