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

Popular posts from this blog

Unable to remove the www from url on https using .htaccess -