What is the difference between locals and globals when using Python's eval()? -
why make difference if variables passed globals or locals python's function eval()?
as described in documenation, python copy __builtins__ globals, if not given explicitly. there must other difference cannot see.
consider following example function. takes string code , returns function object. builtins not allowed (e.g. abs()), functions math package.
def make_fn(code): import math allowed_locals = {v:getattr(math, v) v in filter(lambda x: not x.startswith('_'), dir(math)) } return eval('lambda x: %s' % code, {'__builtins__': none}, allowed_locals) it works expected not using local or global objects:
fn = make_fn('x + 3') fn(5) # outputs 8 but not work using math functions:
fn = make_fn('cos(x)') fn(5) this outputs following exception:
<string> in <lambda>(x) nameerror: global name 'cos' not defined but when passing same mapping globals works:
def make_fn(code): import math allowed = {v:getattr(math, v) v in filter(lambda x: not x.startswith('_'), dir(math)) } allowed['__builtins__'] = none return eval('lambda x: %s' % code, allowed, {}) same example above:
fn = make_fn('cos(x)') fn(5) # outputs 0.28366218546322625 what happens here in detail?
python looks names globals default; names assigned in functions looked locals (so name parameter function or assigned in function).
you can see when use dis.dis() function decompile code objects or functions:
>>> import dis >>> def func(x): ... return cos(x) ... >>> dis.dis(func) 2 0 load_global 0 (cos) 3 load_fast 0 (x) 6 call_function 1 9 return_value load_global loads cos global name, looking in globals namespace. load_fast opcode uses current namespace (function locals) names index (function local namespaces highly optimized , stored c array).
there 3 more opcodes names; load_const (reserved true constants, such none , literal definitions immutable values), load_deref (to reference closure) , load_name. latter @ both locals , globals , used when function code object not optimized, load_name lot slower.
if really wanted cos looked in locals, you'd have force code unoptimised; only works in python 2, adding exec() call (or exec statement):
>>> def unoptimized(x): ... exec('pass') ... return cos(x) ... >>> dis.dis(unoptimized) 2 0 load_const 1 ('pass') 3 load_const 0 (none) 6 dup_top 7 exec_stmt 3 8 load_name 0 (cos) 11 load_fast 0 (x) 14 call_function 1 17 return_value now load_name used cos because python knows, exec() call added name local.
even in case, locals load_name looks into, locals of function itself, , not locals passed eval, parent scope.
Comments
Post a Comment