动态设置局部变量
在Python中,如何动态地设置本地变量(也就是说,变量名是动态变化的)?
7 个回答
(给其他搜索的人一个小提示)
好的,修改 locals()
并不是正确的方法(而修改 globals()
应该是可以的)。与此同时,exec
可能可以用,但它非常慢,所以,就像使用正则表达式一样,我们可能想先用 compile()
编译一下:
# var0 = 0; var1 = 1; var2 = 2
code_text = '\n'.join( "var%d = %d" % (n, n) for n in xrange(3) )
filename = ''
code_chunk = compile( code_text, filename, 'exec' )
# now later we can use exec:
exec code_chunk # executes in the current context
其他人建议可以给 locals()
赋值。但在函数内部这样做是行不通的,因为在函数里访问局部变量是通过 LOAD_FAST
这个指令来实现的,除非 你在函数里有一个 exec
语句。这个 exec
语句可以创建一些在编译时不知道的新变量,所以 Python 被迫通过名字来访问函数里的局部变量,这样给 locals()
赋值就能奏效了。需要注意的是,exec
语句可以不在执行的代码路径中。
def func(varname):
locals()[varname] = 42
return answer # only works if we passed in "answer" for varname
exec "" # never executed
func("answer")
>>> 42
注意:这只在 Python 2.x 中有效。在 Python 3 中,他们取消了这种做法,其他实现(比如 Jython、IronPython 等)也可能不支持。
不过,这其实是个坏主意。你怎么能访问那些变量,如果你不知道它们的名字呢?可能会用 locals()[xxx]
来访问。那么为什么不直接使用你自己的字典,而要去污染 locals()
呢?这样还可能会覆盖掉你函数真正需要的变量。
和其他已经发布的回答不同,你不能直接修改 locals()
,然后期待它能正常工作。
>>> def foo():
lcl = locals()
lcl['xyz'] = 42
print(xyz)
>>> foo()
Traceback (most recent call last):
File "<pyshell#6>", line 1, in <module>
foo()
File "<pyshell#5>", line 4, in foo
print(xyz)
NameError: global name 'xyz' is not defined
修改 locals()
是不确定的。在函数外面,当 locals()
和 globals()
是一样的时候,它会有效;但在函数内部,它通常是无效的。
你可以使用字典,或者在一个对象上设置一个属性:
d = {}
d['xyz'] = 42
print(d['xyz'])
或者如果你喜欢,也可以使用一个类:
class C: pass
obj = C()
setattr(obj, 'xyz', 42)
print(obj.xyz)
编辑:
在不是函数的命名空间(比如模块、类定义、实例)中访问变量通常是通过字典查找的(正如Sven在评论中指出的,有一些例外,比如定义了 __slots__
的类)。函数内部的局部变量可以被优化以提高速度,因为编译器通常提前知道所有的变量名,所以在你调用 locals()
之前并没有字典。
在Python的C实现中,调用 locals()
(在函数内部)会创建一个普通的字典,这个字典是根据当前局部变量的值初始化的。在每个函数内部,调用 locals()
的次数可以是任意的,但每次调用都会用当前的局部变量值更新这个字典。这可能会给人一种印象,认为对字典元素的赋值被忽略了(我最开始是这么写的)。因此,对从 locals()
返回的字典中现有键的修改,只会持续到下一次在同一作用域内调用 locals()
。
在IronPython中,事情的运作方式有些不同。任何在内部调用 locals()
的函数都会使用一个字典来存储它的局部变量,所以对局部变量的赋值会改变字典,而对字典的赋值会改变变量 但 这仅在你明确使用 locals()
这个名字调用时有效。如果你在IronPython中给 locals
函数绑定了一个不同的名字,那么调用它会给你那个名字绑定的作用域中的局部变量,而无法通过它访问到函数的局部变量:
>>> def foo():
... abc = 123
... lcl = zzz()
... lcl['abc'] = 456
... deF = 789
... print(abc)
... print(zzz())
... print(lcl)
...
>>> zzz =locals
>>> foo()
123
{'__doc__': None, '__builtins__': <module '__builtin__' (built-in)>, 'zzz': <built-in function locals>, 'foo': <function foo at 0x000000000000002B>, '__name__': '__main__', 'abc': 456}
{'__doc__': None, '__builtins__': <module '__builtin__' (built-in)>, 'zzz': <built-in function locals>, 'foo': <function foo at 0x000000000000002B>, '__name__': '__main__', 'abc': 456}
>>>
这一切都可能随时改变。唯一可以保证的是,你不能依赖于对 locals()
返回的字典进行赋值的结果。