简体   繁体   English

使用Python的eval()时locals和globals有什么区别?

[英]What is the difference between locals and globals when using Python's eval()?

Why does it make a difference if variables are passed as globals or as locals to Python's function eval() ? 如果将变量作为全局变量或本地变量传递给Python的函数eval(),为什么会有所不同?

As also described in the documenation , Python will copy __builtins__ to globals, if not given explicitly. 正如文档中描述的 ,如果没有明确给出,Python会将__builtins__复制到全局变量。 But there must be also some other difference which I cannot see. 但也必须有一些我看不到的其他差异。

Consider the following example function. 请考虑以下示例函数。 It takes a string code and returns a function object. 它需要一个字符串code并返回一个函数对象。 Builtins are not allowed (eg abs() ), but all functions from the math package. 不允许使用Builtins(例如abs() ),但是math包中的所有函数都是允许的。

def make_fn(code):
    import math
    ALLOWED_LOCALS = {v:getattr(math, v)
        for v in filter(lambda x: not x.startswith('_'), dir(math))
    }
    return eval('lambda x: %s' % code, {'__builtins__': None}, ALLOWED_LOCALS)

It works as expected not using any local or global objects: 它按预期工作,不使用任何本地或全局对象:

   fn = make_fn('x + 3')
   fn(5) # outputs 8

But it does not work using the math functions: 但它使用math函数不起作用:

   fn = make_fn('cos(x)')
   fn(5)

This outputs the following exception: 这会输出以下异常:

   <string> in <lambda>(x)
   NameError: global name 'cos' is not defined

But when passing the same mapping as globals it works: 但是当传递与globals相同的映射时,它可以工作:

def make_fn(code):
   import math
   ALLOWED = {v:getattr(math, v)
      for v in filter(lambda x: not x.startswith('_'), dir(math))
   }
   ALLOWED['__builtins__'] = None
   return eval('lambda x: %s' % code, ALLOWED, {})

Same example as above: 与上面相同的例子:

   fn = make_fn('cos(x)')
   fn(5) # outputs 0.28366218546322625

What happens here in detail? 这里有什么细节?

Python looks up names as globals by default; Python默认将名称查找为全局变量; only names assigned to in functions are looked up as locals (so any name that is a parameter to the function or was assigned to in the function). 只有在函数中分配的名称才会被查找为本地符号(因此任何名称都是函数的参数或在函数中赋值)。

You can see this when you use the dis.dis() function to decompile code objects or functions: 当您使用dis.dis()函数反编译代码对象或函数时,您可以看到这一点:

>>> 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 as a global name, only looking in the globals namespace. LOAD_GLOBALcos作为全局名称加载,仅查看全局命名空间。 The LOAD_FAST opcode uses the current namespace (function locals) to look up names by index (function local namespaces are highly optimized and stored as a C array). LOAD_FAST操作码使用当前命名空间(函数本地)来按索引查找名称(函数本地命名空间被高度优化并存储为C数组)。

There are three more opcodes to look up names; 查找名称还有三个操作码; LOAD_CONST (reserved for true constants, such as None and literal definitions for immutable values), LOAD_DEREF (to reference a closure) and LOAD_NAME . LOAD_CONST (为真常量保留,例如None和不可变值的文字定义), LOAD_DEREF (引用闭包)和LOAD_NAME The latter does look at both locals and globals and is only used when a function code object could not be optimized, as LOAD_NAME is a lot slower. 后者确实查看本地和全局,并且仅在无法优化函数代码对象时使用,因为LOAD_NAME要慢得多。

If you really wanted cos to be looked up in locals , you'd have to force the code to be unoptimised; 如果你真的想要在locals查找cos ,你必须强制代码不被优化; this only works in Python 2, by adding a exec() call (or exec statement): 这只适用于Python 2,通过添加exec()调用(或exec语句):

>>> 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 is used for cos because for all Python knows, the exec() call added that name as a local. 现在LOAD_NAME用于cos因为对于所有Python都知道, exec()调用将该名称添加为本地名称。

Even in this case, the locals LOAD_NAME looks into, will be the locals of the function itself, and not the locals passed to eval , which are for only for the parent scope. 即使在这种情况下, LOAD_NAME查看的本地人也将是函数本身的本地LOAD_NAME ,而不是传递给eval的本地eval ,它们仅用于父作用域。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM