如何在Python中动态设置本地变量(其中变量名是动态的)?
与其他已发布的答案相反,您不能直接修改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)
编辑:
通常使用字典查找来访问不是函数的命名空间中的变量(因此是模块、类定义、实例)。函数局部变量可以进行速度优化,因为编译器(通常)提前知道所有名称,所以在调用locals()
之前没有字典。
在Python的C实现中,locals()
(从函数内部调用)创建一个普通字典,其初始化为局部变量的当前值。在每个函数内,对locals()
的任意数量调用将返回相同的字典,但每次调用locals()
都会使用局部变量的当前值更新它。这可能会给人一种印象,即字典的元素赋值被忽略了(我最初写的是这种情况)。因此,对于从locals()
返回的现有键的修改仅持续到同一作用域中下一次调用locals()
。
在IronPython中情况有些不同。任何调用其内部的locals()
的函数都会使用一个字典作为其局部变量,因此对局部变量的赋值将更改字典,对字典的赋值将更改变量但是只有在将不同的名称绑定到IronPython中的locals
函数时,才会这样做。如果绑定一个不同的名称到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()
返回的字典进行赋值的结果。
locals()
的返回值在 CPython 2.x 和 3.x 中是标准字典,可以像通常一样修改(但对本地作用域的更改不会传播回去)。__slots__
的类的实例。dict
中即可。 - Duncanlocals()
字典,否则无法访问它,并且不能确定您没有覆盖或隐藏另一个局部变量甚至是全局名称。使用dict
可以安全地将任何字符串存储为键。 - Duncanexec
或覆盖locals()
,就会冒着你提到的风险(例如覆盖变量)。如果确切地知道将变量加载到本地并且知道名称,那么稍后就可以使用这些变量,编译器如果没有预先定义它们就会出现问题。我个人并不觉得有什么大不了的,一个人总是可以重新编写全局变量,比如for ='breaking for keyword'
,而不需要覆盖本地变量。我想我可能没有意识到真正的问题或者什么的... - Charlie Parkerfor
这样的保留字用于赋值。 - Duncan有人建议使用locals()
进行赋值。但是在函数内部,这种方法不起作用,因为局部变量是使用LOAD_FAST
操作码访问的,除非函数内有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()
不是正确的方法 (而修改 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()
:locals()['foo'] = 'bar'
但更好的方法是创建一个字典,将所有的动态变量名作为字典键:
d = {}
for some in thing:
d[some] = 'whatever'
locals()
。 - Duncanlocals()['foo'] = 'bar'
在 Python 2.7 和 3 中都可以使用,这个在某个时候被改变了吗? - Georg Pfolz我花了最近几个小时,尝试绕过函数闭包的缺失,最终得出以下解决方案,希望能帮到你:
common_data = ...stuff...
def process(record):
...logic...
def op():
for fing in op.func_dict: # Key line number 1
exec(fing + " = op.func_dict[fing]") # Key line number 2
while common_data.still_recieving:
with common_data._lock:
if common_data.record_available:
process(common_data.oldest_record)
time.sleep(1.0)
op.func_dict.update(locals()) # Key line number 3
threading.Thread(target = op).start()
...
这个例子比较生硬/刻意,但如果有很多本地人或者你还在原型阶段,这种模式会变得很有用。主要是因为所有数据存储区都被复制或移动以处理回调委托等问题,让我感到有些不满。
DictionaryA = {'No Rating': ['Hobbit', 'Movie C', 'Movie G'],
'Forget It': ['Avenger', 'Movie B'],
'Must See': ['Children of Men', 'Skyfall', 'Movie F'],
'3': ['X-Men', 'Movie D'],
'2': ['Captain America', 'Movie E'],
'4': ['Transformers', 'Movie A']}
NewDictionary1 = {'No Rating': ['Hobbit', 'Movie C', 'Movie G']}
NewDictionary2 = {'Forget It': ['Avenger', 'Movie B']}
NewDictionary3 = {'Must See': ['Children of Men', 'Skyfall', 'Movie F']}
dics = [{k:v} for k,v in DictionaryA.iteritems()]
将被输出为:
[{'Must See': ['Children of Men', 'Skyfall', 'Movie F']}, {'Forget It': ['Avenger', 'Movie B']}, {'No Rating': ['Hobbit', 'Movie C', 'Movie G']}, {'3': ['X-Men', 'Movie D']}, {'2': ['Captain America', 'Movie E']}, {'4': ['Transformers', 'Movie A']}]
但是,为了精确地声明变量,我们可以选择:
>>> i=0
>>> lcl = locals()
>>> for key,val in DictionaryA.iteritems():
lcl["Dict" + str(i)] = {key:val}
i += 1
可以看到前三个Dict
变量:
>>> Dict0
{'Must See': ['Children of Men', 'Skyfall', 'Movie F']}
>>> Dict1
{'Forget It': ['Avenger', 'Movie B']}
>>> Dict2
{'No Rating': ['Hobbit', 'Movie C', 'Movie G']}
globals()
中:>>> glb = globals()
>>> for key,val in DictionaryA.iteritems():
glb["Dict" + str(i)] = {key:val}
i += 1
你可以使用本地字典,并将所有动态绑定作为项放入字典中。然后,知道这样一个“动态变量”的名称,您可以使用名称作为键来获取其值。
locals()
只是调用PyFrame_FastToLocals来创建当前值的字典视图。如果使用ctypes
在当前帧(sys._getframe(0)
)上调用PyFrame_LocalsToFast,可以根据这个字典动态更新快速局部变量。 - Eryk Sun