使用 `exec` 调用时如何更新局部变量?

问题描述 投票:0回答:3

我以为这会打印 3,但它打印 1:

# Python3

def f():
    a = 1
    exec("a = 3")
    print(a)

f()
# 1 Expected 3
python python-3.x exec local-variables
3个回答
87
投票

这个问题在 Python3 bug 列表 中有所讨论。 最终,要获得这种行为,您需要执行以下操作:

def foo():
    ldict = {}
    exec("a=3",globals(),ldict)
    a = ldict['a']
    print(a)

如果您查看 exec

 上的 Python3 文档,您将看到以下注释:

默认局部变量的行为如下函数

locals()

所述:
不应尝试修改默认局部变量字典。如果您需要在函数 exec() 返回后查看代码对局部变量的影响,请传递显式局部变量字典。

这意味着单参数

exec

无法安全地执行任何绑定局部变量的操作,包括变量赋值、导入、函数定义、类定义等。如果它使用
global
声明,它可以分配给全局变量,但不是当地人。

回顾

错误报告中的具体消息,Georg Brandl 说:

动态修改函数的局部变量不是 可能没有几个后果:

通常,函数局部变量不是 存储在字典中,而是一个数组,其索引确定于 从已知语言环境编译时间。 这至少与新的冲突 由 exec 添加的当地人。 旧的 exec 语句规避了这一点,因为 编译器知道如果发生没有全局/局部参数的执行 在函数中,该名称空间将是“未优化的”,即不使用 当地人数组。 由于 exec() 现在是一个普通函数,编译器不会 不知道“exec”可能绑定到什么,因此不能治疗 特别.

重点是我的。

所以要点是,Python3 可以通过默认情况下

允许这种行为来更好地优化局部变量的使用。

为了完整起见,正如上面评论中提到的,这

确实在Python 2.X中按预期工作:

Python 2.6.2 (release26-maint, Apr 19 2009, 01:56:41) [GCC 4.3.3] on linux2 Type "help", "copyright", "credits" or "license" for more information. >>> def f(): ... a = 1 ... exec "a=3" ... print a ... >>> f() 3
    

5
投票
如果您在方法内部,您可以这样做:

# python 2 or 3 class Thing(): def __init__(self): exec('self.foo = 2') x = Thing() print(x.foo)

您可以在这里阅读更多相关信息


4
投票
无法以这种方式使用

exec

 更改函数内的局部变量的原因,以及 
exec
 为何如此行事,可以总结如下:

  1. exec
     是一个函数,它与调用它的最内部作用域的作用域共享其局部作用域。 
  2. 每当您在函数范围内定义一个新对象时,都可以在其本地命名空间中访问该对象,即它将修改
  3. local()
     字典。当您在 
    exec
     中定义一个新对象时,它的作用大致相当于以下内容:

from copy import copy class exec_type: def __init__(self, *args, **kwargs): # default initializations # ... self.temp = copy(locals()) def __setitem__(self, key, value): if var not in locals(): set_local(key, value) self.temp[key] = value

temp

 是一个临时命名空间,每次实例化后都会重置(每次调用 
exec
 时)。


    Python 开始从本地命名空间查找名称。这被称为 LEGB 方式。 Python 从本地命名空间开始,然后查找封闭范围,然后全局,最后查找内置命名空间内的名称。
更全面的示例如下:

g_var = 5 def test(): l_var = 10 print(locals()) exec("print(locals())") exec("g_var = 222") exec("l_var = 111") exec("print(locals())") exec("l_var = 111; print(locals())") exec("print(locals())") print(locals()) def inner(): exec("print(locals())") exec("inner_var = 100") exec("print(locals())") exec("print([i for i in globals() if '__' not in i])") print("Inner function: ") inner() print("-------" * 3) return (g_var, l_var) print(test()) exec("print(g_var)")

输出:

{'l_var': 10} {'l_var': 10}

当地人都一样。

{'l_var': 10, 'g_var': 222}

添加

g_var

 并更改 
l_var
 后,它仅添加 
g_var
 并保持 
l_var
 不变。

{'l_var': 111, 'g_var': 222}

l_var

 已更改,因为我们正在一次实例化中更改和打印本地变量(一次调用 exec)。

{'l_var': 10, 'g_var': 222} {'l_var': 10, 'g_var': 222}

在函数的局部变量和 exec 的局部变量中,

l_var

 保持不变,并添加了 
g_var

Inner function: {} {'inner_var': 100} {'inner_var': 100}

inner_function

的本地与exec的本地相同。

['g_var', 'test']

global 仅包含

g_var

 和函数名(排除特殊方法后)。

--------------------- (5, 10) 5
    
© www.soinside.com 2019 - 2024. All rights reserved.