Consider the following example:
i=7
j=8
k=10
def test():
i=1
j=2
k=3
return dict((name,eval(name)) for name in [\'i\',\'j\',\'k\'])
Generators are implemented as function scopes:
The scope of names defined in a class block is limited to the class block; it does not extend to the code blocks of methods – this includes generator expressions since they are implemented using a function scope.
So, the generator inside the dict()
constructor has its own locals()
dictionary. Now let's take a look at Py_eval's source code, specially when both globals()
and locals()
are None:
if (globals == Py_None) {
globals = PyEval_GetGlobals();
if (locals == Py_None)
locals = PyEval_GetLocals();
}
So, for your example PyEval_GetLocals() will be empty at the moment the loop is executing and globals()
will be the global dictionary. Note that i
, j
and k
defined inside the function are not in local scope of generator, rather they are in its enclosing scope:
>>> dict((name,eval(name, globals(), {})) for name in ['i', 'j', 'k'])
{'i': 7, 'k': 10, 'j': 8}
This occurs because the generator expression has a different scope to the function:
>>> def test():
i, j, k = range(1, 4)
return dict((j, locals()) for _ in range(i))
>>> test()
{2: {'.0': <listiterator object at 0x02F50A10>, 'j': 2, '_': 0}}
Using j
inside the scope binds it from the function, as that's the nearest enclosing scope, but i
and k
are not locally bound (as k
isn't referenced and i
is only used to create the range
).
Note that you can avoid this issue with:
return dict(i=i, j=j, k=k)
or a dictionary literal:
return {'i': i, 'j': j, 'k': k}