使用“exec”调用时,如何更新局部变量?
- 2024-12-04 08:56:00
- admin 原创
- 152
问题描述:
我以为这会打印 3,但它打印了 1:
def f():
a = 1
exec("a = 3")
print(a)
f()
我正在使用 Python 3。在 Python 2 中,它可以按预期工作(打印 3)。
解决方案 1:
Python3 错误列表中对此问题进行了一些讨论。最终,要获得此行为,您需要执行以下操作:
def foo():
ldict = {}
exec("a = 3", globals(), ldict)
a = ldict['a']
print(a)
如果你查看Python3 文档exec
,你会看到以下说明:
默认局部变量的作用如下所述
locals()
:*不应尝试修改默认局部变量字典。如果您需要在函数返回后查看代码对局部变量的影响,请传递显式局部变量字典。*exec()
这意味着单参数exec
不能安全地执行任何绑定局部变量的操作,包括变量赋值、导入、函数定义、类定义等。如果它使用声明global
,它可以分配给全局变量,但不能分配给局部变量。
回顾错误报告中的具体消息,Georg Brandl 说:
在没有几个后果的情况下,动态修改函数的局部变量是不可能的:通常,函数局部变量不存储在字典中,而是存储在数组中,其索引是在编译时根据已知的本地设置确定的。这至少与 exec 添加的新局部变量相冲突。
然后,关于 Python 2:
旧的 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
解决方案 2:
无法exec
以此方式更改函数内的局部变量的原因以及为什么exec
会这样操作,可以总结如下:
exec
是一个与调用它的最内层作用域共享其本地作用域的函数。每当您在函数范围内定义新对象时,它都可以在其本地命名空间中访问,即它将修改字典
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
解决方案 3:
如果你在方法内部,你可以这样做:
# python 2 or 3
class Thing():
def __init__(self):
exec('self.foo = 2')
x = Thing()
print(x.foo)
您可以在此处阅读更多相关信息