【问题标题】:eval globals and locals argument do not work as expectedeval globals 和 locals 参数不能按预期工作
【发布时间】:2017-04-11 14:49:27
【问题描述】:

语句 eval'd 似乎实际上并未在具有相应全局对象和局部对象的环境中执行。

def f(x):
    return g(x)*3
def g(x):
    return x**2
funcs = {"f":f,"g":g}
del globals()['g'] # to keep them out of the global environment
del globals()['f']
eval("f(2)",globals(),funcs)

错误:

Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<string>", line 1, in <module>
  File "<stdin>", line 2, in f
NameError: name 'g' is not defined

更新:

更多说明:

>>> exec("print(globals()['g'])",{**globals(),**funcs})
<function g at 0x7feb627aaf28>
>>> eval("f(2)",{**globals(),**funcs})
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<string>", line 1, in <module>
  File "<stdin>", line 2, in f
NameError: name 'g' is not defined

编辑

这不是this question 的副本。即使作为全局函数传递,函数 g 也无法查找。

【问题讨论】:

  • 这不是那个问题的重复,因为 NameError 也发生在全局变量中。这不是全局与本地的问题

标签: python python-3.5


【解决方案1】:

可以通过查看使用dis.dis(f) 函数f()
定义编译的字节码来发现问题:

  7           0 LOAD_GLOBAL              0 (g)
              2 LOAD_FAST                0 (x)
              4 CALL_FUNCTION            1
              6 LOAD_CONST               1 (3)
              8 BINARY_MULTIPLY
             10 RETURN_VALUE

如您所见,第一条指令尝试加载名为g全局

使其工作的一种方法是将g() 设为本地函数:

def f(x):
    def g(x):
        return x**2

    return g(x)*3

funcs = {"f": f}
del globals()['f'] # to keep it  out of the global environment
print(eval("f(2)", globals(), funcs))  # -> 12

这是修改后的f() 的字节码,用于比较:

  8           0 LOAD_CONST               1 (<code object g at 0x00546288, file "test.py">)
              2 LOAD_CONST               2 ('f.<locals>.g')
              4 MAKE_FUNCTION            0
              6 STORE_FAST               1 (g)

 11           8 LOAD_FAST                1 (g)
             10 LOAD_FAST                0 (x)
             12 CALL_FUNCTION            1
             14 LOAD_CONST               3 (3)
             16 BINARY_MULTIPLY
             18 RETURN_VALUE

【讨论】:

  • >>> exec("print(globals()['g'])",{**globals(),**funcs}) 似乎暗示有实际上,环境应该可以访问一个名为“g”的全局变量吗?如果 eval/exec 函数不使用它来查找对象,那么能够传入全局对象有什么意义?确实,如果不在 globals() 中,它在哪里寻找 g?
  • @Scott:当eval("f(2)", globals(), funcs) 执行时,有一个g() 函数,但它在字典中作为locals 参数传递给eval(),所以其字节码开头的LOAD_GLOBAL 失败。
  • 怎么样 >>> eval("f(2)",{**globals(),**funcs})?那也找不到g。
  • @Scott:好的,我明白你的意思了——并且同意 那个 没有意义(也不同意文档)。也许它应该被报告为一个错误。
【解决方案2】:

当您定义f 时,它决定对g 的引用是全局名称查找。此外,它在定义时保留了对有效全局环境的引用(这让您可以在其他模块中调用函数,而不会剥夺它们原来的全局变量)。

当您随后删除 g 时,您从根本上破坏了 f - 此对 g 的全局查找现在将失败。

exec 调用的全局/本地环境参数对已编译的函数 f 没有任何影响。它们只影响您执行的实际文本:"f(2)"。换句话说,实际使用您提供的环境的唯一名称查找是f 本身。

【讨论】:

  • 有什么方法可以在我提供的环境中进行 eval(f) 吗?
  • 好吧,你可以exec 函数体(而不是调用函数),或者使用types 模块构造一个新的函数对象,其func_globals 不同于原本的。或者您可以完全避免这种复杂的混乱:如果需要自定义函数的操作,则将这些细节作为参数传递
  • 好吧,为了把它放在上下文中,我正在组合一个分布式计算系统,它允许远程执行 f.即。我在 jupyter 笔记本上创建了一堆代码,发现 f 需要数周时间,然后将 f 及其所有要求发送到 big mean 服务器, big mean 服务器对其进行处理并交回结果。我的想法是我可以将所有内容都发送到 globals() 中,然后它就会执行。这显然是不正确的。 =c)
  • types.FunctionType(f.__code__, funcs, f.__name__, f.__defaults__, f.__closure__) 做到了。我们有责任开源这整个混乱。当我们这样做时,我会以某种方式在这个线程上发布一个链接。非常感谢。
猜你喜欢
  • 1970-01-01
  • 2016-08-25
  • 2022-01-26
  • 2019-02-18
  • 2016-02-06
  • 2020-06-14
  • 2019-03-06
  • 1970-01-01
  • 2016-09-09
相关资源
最近更新 更多