动态设置局部变量

118 投票
7 回答
99052 浏览
提问于 2025-04-17 05:45

在Python中,如何动态地设置本地变量(也就是说,变量名是动态变化的)?

7 个回答

8

(给其他搜索的人一个小提示)

好的,修改 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
30

其他人建议可以给 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() 呢?这样还可能会覆盖掉你函数真正需要的变量。

88

和其他已经发布的回答不同,你不能直接修改 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() 返回的字典进行赋值的结果。

撰写回答