Why doesn't eval of generator expression work with locals?

Hendrik van Rooyen mail at microcorp.co.za
Fri Jan 30 10:08:29 CET 2009


"Gabriel Genellina" <gagsl-py2 at yahoo.com.ar> wrote:

Of course this is clearly stated in the Language Reference "Variables used
in the generator expression are evaluated lazily in a separate scope when
the next() method is called for the generator object (in the same fashion
as for normal generators). However, the in expression of the leftmost for
clause is immediately evaluated in the current scope..." -- but this
behaviour is still surprising and not obvious to me. ("not obvious" means
that things could have been different, choosing this was a design
decision).

I am not so sure that it could have been done differently -
I see it something like this:  (going back to almost your
original example, and reversing the position of globals
and locals to make it shorter)

>>> def foo(things):
 for thing in things:
  yield thing()    #it is obvious this is in the local scope of foo

>>> boo = foo([locals,globals])
>>> boo.next()
{'thing': <built-in function locals>, 'things': [<built-in function locals>,
<built-in function globals>]}
>>>
and so it is, when you feed it the locals function

Your other example would have been something like this:

>>> def bar(things):
 for thing in things:
  yield thing      # this just returns it unchanged

>>> baz = bar([locals(),globals()])  # here we are still in the outer scope

>>> baz.next()
{'bar': <function bar at 0x011BB5B0>, '__builtins__': <module '__builtin__'
(built-in)>, 'baz': <generator object at 0x011D1CD8>, '__file__':
'E:\\Python24\\Lib\\idlelib\\idle.pyw', 'idlelib': <module 'idlelib' from
'E:\PYTHON24\lib\idlelib\__init__.pyc'>, 'boo': <generator object at
0x011D14B8>, '__name__': '__main__', 'foo': <function foo at 0x011BDFB0>,
'__doc__': None}
>>>
and we get the top level locals back, as expected.

Now I don't think that you could really do it differently -
the right hand side of the generator expression is exactly
like my passed argument "things", in all cases as far as
I can see, and this means that the right hand side is
evaluated when it is "passed", and the left hand side
is whatever is done in the "for thing in things:" loop.
All the generator expression does is that it saves you
the trouble of defining the function - it kind of does it
for you, and calls it, and returns the generator object,
and throws the function away, all in one hit. (this is not
necessarily the real mechanism, but the effect is exactly
as if it were)

I can't think of a way to do it differently - you have
to make the "things" you want to iterate over, before
you can do the iteration, and this is the cause of the
timing difference and the outer level evaluation of the
"passed" argument, and the different scope comes
from the scope of the "ghost" function.

Or is this view too simple?

- Hendrik






More information about the Python-list mailing list