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

mario ruggier mario.ruggier at gmail.com
Fri Jan 16 00:05:18 CET 2009


On Jan 15, 11:35 pm, Terry Reedy <tjre... at udel.edu> 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.  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.
>
>
>
> >>>> def f(): [i for i in [1]]
> > ...
> >>>> dis.dis(f)
> >   1           0 BUILD_LIST               0
> >               3 DUP_TOP
> >               4 STORE_FAST               0 (_[1])
> >               7 LOAD_CONST               1 (1)
> >              10 BUILD_LIST               1
> >              13 GET_ITER
> >         >>   14 FOR_ITER                13 (to 30)
> >              17 STORE_FAST               1 (i)
> >              20 LOAD_FAST                0 (_[1])
> >              23 LOAD_FAST                1 (i)
> >              26 LIST_APPEND
> >              27 JUMP_ABSOLUTE           14
> >         >>   30 DELETE_FAST              0 (_[1])
> >              33 POP_TOP
> >              34 LOAD_CONST               0 (None)
> >              37 RETURN_VALUE
>
> 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
>
> Running OP code in 3.0 with print ()s added gives
>
> pre 0.a 1.b 2.c 3.d  post
>
> Traceback (most recent call last):
>    File "C:\Programs\Python30\misc\temp7.py", line 32, in <module>
>      """ % gie)
>    File "C:\Programs\Python30\misc\temp7.py", line 8, in __getitem__
>      return eval(expr, self.globals, self.locals)
>    File "<string>", line 7, in <module>
>    File "<string>", line 7, in <listcomp>
>    File "C:\Programs\Python30\misc\temp7.py", line 12, in ts
>      return ts % self
>    File "C:\Programs\Python30\misc\temp7.py", line 8, in __getitem__
>      return eval(expr, self.globals, self.locals)
>    File "<string>", line 2, in <module>
>    File "<string>", line 1, in <listcomp>
> NameError: global name 'i' is not defined
>
> > If you manage to run two nested listcomps in the same namespace you get a
> > name clash and the inner helper variable overwrites/deletes the outer:
>
> >>>> def xeval(x): return eval(x, ns)
> > ...
> >>>> ns = dict(xeval=xeval)
> >>>> xeval("[xeval('[k for k in ()]') for i in (1,)]")
> > Traceback (most recent call last):
> >   File "<stdin>", line 1, in <module>
> >   File "<stdin>", line 1, in xeval
> >   File "<string>", line 1, in <module>
> > NameError: name '_[1]' is not defined
>
> Which Python?  3.0 prints "[[]]"! But I think the nested listcomp *is*
> in a separate namespace here.  I will leave it to you or OP to disect
> how his and your code essentially differ from 3.0 (and maybe 2.6)
> implementation's viewpoint.

I was testing on 2.6, but running it thru 2.4 and 2.5 it seems
behaviour is the same there. For 3.0 it does change... and there seems
not to be the "_[1]" key defined, and, what's more, it gives a:
NameError: name 'j' is not defined.

In any case, that was an exploration to get a feeling for how the
listcomps behave (performance) if evaluated directly as opposed to
doing the equivalent from within a function. It turned out to be
slower, so I moved on... but, should it have been faster, then
differences between how the different python versions handle list
comps internally would have been a next issue to address.

I think the globals dict is not touched by eval'ing a list comp... it
is any not "constant" as such, just that it is not affected by
evaluations (unless the python application decides to affect it in
some way or another). But, evaluating a template by definition does
not change the globals dict.

> Terry Jan Reedy



More information about the Python-list mailing list