List comprehension - NameError: name '_[1]' is not defined ?

Peter Otten __peter__ at web.de
Fri Jan 16 10:31:37 CET 2009


Terry Reedy wrote:

> Peter Otten wrote:
> 
>> List comprehensions delete the helper variable after completion:
> 
> I do not believe they did in 2.4.  Not sure of 2.5.  

As Mario said, 2.4, 2.5, and 2.6 all show the same behaviour.

> There is certainly 
>   a very different implementation in 3.0 and, I think, 2.6.  OP
> neglected to mention Python version he tested on. Code meant to run on
> 2.4 to 3.0 cannot depend on subtle listcomp details.

3.0 behaves different. Like generator expressions listcomps no longer leak
the loop variable, and this is implemented by having each listcomp execute
as a nested function:

> In 3.0
>  >>> def f(): [i for i in [1]]
> 
>  >>> import dis
>  >>> dis.dis(f)
>    1           0 LOAD_CONST               1 (<code object <listcomp> at
> 0x01349BF0, file "<pyshell#12>", line 1>)
>                3 MAKE_FUNCTION            0
>                6 LOAD_CONST               2 (1)
>                9 BUILD_LIST               1
>               12 GET_ITER
>               13 CALL_FUNCTION            1
>               16 POP_TOP
>               17 LOAD_CONST               0 (None)
>               20 RETURN_VALUE

This is more robust (at least I can't think of a way to break it like the
2.x approach) but probably slower due to the function call overhead. The
helper variable is still there, but the possibility of a clash with another
helper is gone (disclaimer: I didn't check this in the Python source) so
instead of

# 2.5 and 2.6 (2.4 has the names in a different order)

>>> def f():
...     [[i for i in ()] for k in ()]
...
>>> f.func_code.co_varnames
('_[1]', 'k', '_[2]', 'i')

we get

# 3.0

>>> def f():
...     [[i for i in ()] for k in ()]
...
>>> f.__code__.co_varnames
()

The variables are gone from f's scope, as 3.x listcomps no longer leak their
loop variables.

>>> f.__code__.co_consts
(None, <code object <listcomp> at 0x2b8d7f6d7530, file "<stdin>", line 2>,
())
>>> outer = f.__code__.co_consts[1]
>>> outer.co_varnames
('.0', '_[1]', 'k')

Again the inner listcomp is separated from the outer.

>>> outer.co_consts
(<code object <listcomp> at 0x2b8d7f6d26b0, file "<stdin>", line 2>, ())
>>> inner = outer.co_consts[0]
>>> inner.co_varnames
('.0', '_[1]', 'i')


Peter



More information about the Python-list mailing list