动态设置局部变量

如何在 Python 中动态设置局部变量(其中变量名是动态的) ?

89869 次浏览

你可以直接修改 locals():

locals()['foo'] = 'bar'

但是一个更好的方法是使用一些 dict 将所有动态变量名保存为字典键:

d = {}
for some in thing:
d[some] = 'whatever'

与已经发布的其他答案相反,你不能直接修改 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 locals()的 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()返回的字典的结果。

其他人建议分配到 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。他们在 Python3中摒弃了这种愚蠢的做法,其他实现(Jython、 IronPython 等)可能也不支持它。

不过这不是个好主意。如果您不知道变量的名称,您将如何访问它们?可能是 locals()[xxx]。那么,为什么不使用自己的字典,而不是污染 locals()(并冒险覆盖函数实际需要的变量) ?

您可以使用本地字典并将所有动态绑定作为项目放入字典中。然后知道这样一个“动态变量”的名称,你可以使用这个名称作为键来获取它的值。

我花了... 几个小时,试图破解函数闭包的缺失,我想出了这个,可能会有帮助:

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()


...

这是一个相当笨拙/人为的示例,但是如果有很多局部变量,或者您仍然处于原型开发过程中,那么这个模式将变得非常有用。大多数时候,我只是对所有为了处理回调委托而被复制或移动的数据存储感到痛苦,等等。

(Just a quick note for others googlin')

修改 locals() is not the way to go(同时修改 globals() is supposed to work)。与此同时,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

假设我们有下面这本词典:

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']}

I want to create new dictionaries as below:

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

正如可以看到的前3个 Dict变量:

>>> Dict0
{'Must See': ['Children of Men', 'Skyfall', 'Movie F']}
>>> Dict1
{'Forget It': ['Avenger', 'Movie B']}
>>> Dict2
{'No Rating': ['Hobbit', 'Movie C', 'Movie G']}

As mentioned by others, if you want to put it in a function you should add it to the globals():

>>> glb = globals()
>>> for key,val in DictionaryA.iteritems():
glb["Dict" + str(i)] = {key:val}
i += 1