scope of eval function in python
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}