我想编写一个接收本地命名空间字典并更新它的函数。像这样:
def UpdateLocals(local_dict):
d = {'a':10, 'b':20, 'c':30}
local_dict.update(d)
当我从交互式python shell调用此函数时,它可以正常工作,如下所示:
a = 1
UpdateLocals(locals())
# prints 20
print a
但是,当我从函数内部调用UpdateLocals
时,它没有达到我的预期:
def TestUpdateLocals():
a = 1
UpdateLocals(locals())
print a
# prints 1
TestUpdateLocals()
如何使第二种情况像第一种情况一样工作?
更新:
Aswin的解释很有意义,对我很有帮助。但是我仍然想要一种更新局部变量的机制。在我找出一个不那么难看的方法之前,我将做以下几点:
def LoadDictionary():
return {'a': 10, 'b': 20, 'c': 30}
def TestUpdateLocals():
a = 1
for name, value in LoadDictionary().iteritems():
exec('%s = value' % name)
当然,字符串语句的构造可以自动化,并且可以向用户隐藏细节。
答案 0 :(得分:1)
你问了一个很好的问题。事实上,更新局部变量的能力对于保存和加载用于机器学习或游戏的数据集非常重要和关键。然而,大多数 Python 语言的开发人员还没有意识到它的重要性。他们过于关注一致性和优化,但这也很重要。
假设你正在开发一个游戏或运行一个深度神经网络(DNN),如果所有局部变量都是可序列化的,那么保存整个游戏或 DNN 可以简单地放在一行中作为 print(locals())
,并且加载整个游戏或 DNN 可以简单地放在一行中作为 locals().update(eval(sys.stdin.read()))
。
目前,globals().update(...)
立即生效,但 locals().update(...)
不起作用,因为 Python 文档说:
默认局部变量的行为如下面的函数 locals() 所述: 不应该修改默认的 locals 字典 尝试过。如果您需要查看,请传递一个显式的 locals 字典 函数 exec() 返回后代码对局部变量的影响。
为什么他们以这种方式设计 Python 是因为优化并将 exec
语句整合到一个函数中:
如果没有,则无法动态修改函数的局部变量 几个后果:通常,函数局部变量不存储在 字典,而是一个数组,其索引在编译时确定 从已知的地方。这至少与添加的新当地人相冲突 通过执行。旧的 exec 语句绕过了这一点,因为 编译器知道如果没有全局变量/局部变量参数的 exec 发生在 一个函数,该命名空间将是“未优化的”,即不使用 本地人数组。由于 exec() 现在是一个普通函数,编译器会 不知道“exec”可能绑定到什么,因此不能处理的是 特别。
由于 global().update(...)
有效,以下代码将在根命名空间(即,在任何函数之外)工作,因为 locals() 与根命名空间中的 globals() 相同:
locals().update({'a':3, 'b':4})
print(a, b)
但这在函数内部不起作用。
但是,作为黑客级别的 Python 程序员,我们可以使用 sys._getframe(1).f_locals
代替 locals()
。从我到目前为止的测试来看,在 Python 3 上,以下代码始终有效:
def f1():
sys._getframe(1).f_locals.update({'a':3, 'b':4})
print(a, b)
f1()
但是,sys._getframe(1).f_locals
在根命名空间中不起作用。
答案 1 :(得分:0)
此处未更新本地因素,因为在第一种情况下,声明的变量具有全局范围。但是当在函数内部声明时,变量会失去它之外的范围。
因此,UpdateLocals
函数中未更改locals()的原始值。
PS:这可能与您的问题无关,但使用驼峰案例并不是Python的好习惯。尝试使用其他方法。
update_locals()
代替UpdateLocals()
修改要回答评论中的问题:
有一种称为系统堆栈的东西。在执行代码期间,此系统堆栈的主要工作是管理局部变量,确保在完成执行被调用函数等操作后控件返回到正确的语句。
因此,每次进行函数调用时,都会在该堆栈中创建一个新条目, 其中包含控件必须在return语句后返回的行号(或指令号),以及一组新的局部变量。
当控件在函数内部时,局部变量将从堆栈条目中获取。因此,两个函数中的局部集合不相同。当控件退出函数时,将弹出堆栈中的条目。因此,您在函数内所做的更改将被删除,除非并且直到这些变量具有全局范围。