关于python:动态设置局部变量

Dynamically set local variable

本问题已经有最佳答案,请猛点这里访问。

如何在python中动态设置局部变量?

(变量名是动态的)

最新消息:我知道这不是一个好的做法,而且这些言论是合法的,但这并不意味着这是一个坏问题,只是一个更理论化的问题——我不明白为什么这会证明投反对票是正当的。


与其他已发布的答案相反,您不能直接修改locals(),并期望它能够工作。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
>>> 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()相同时,它将工作;在一个函数内部,它通常不工作。

使用字典或在对象上设置属性:

1
2
3
d = {}
d['xyz'] = 42
print(d['xyz'])

或者,如果您愿意,使用一个类:

1
2
3
4
5
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函数,那么调用它将为绑定名称的作用域提供局部变量,并且无法通过它访问函数局部变量:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
>>> 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可以超出执行的代码路径。

1
2
3
4
5
6
7
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()首先:

1
2
3
4
5
6
7
8
9
# var0 = 0; var1 = 1; var2 = 2
code_text = '
'
.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


我已经花了最后的时间…我猜,几个小时后,我试图绕过缺少函数闭包的问题,我想到了这一点,这可能有助于:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
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()

...

这是一个非常沉重的/人为的例子,但是如果有很多本地人,或者您仍在进行原型化的过程中,那么这个模式就变得有用了。大多数情况下,我只是对为处理回调委托而复制或移动的所有数据存储感到苦恼。


您可以直接修改locals()

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

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

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


假设我们有以下字典:

1
2
3
4
5
6
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']}

我想创建如下的新词典:

1
2
3
NewDictionary1 = {'No Rating': ['Hobbit', 'Movie C', 'Movie G']}
NewDictionary2 = {'Forget It': ['Avenger', 'Movie B']}
NewDictionary3 = {'Must See': ['Children of Men', 'Skyfall', 'Movie F']}

Onelier-:

1
dics = [{k:v} for k,v in DictionaryA.iteritems()]

将输出到:

1
[{'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']}]

但是为了精确地声明变量,我们可以使用:

1
2
3
4
5
>>> i=0
>>> lcl = locals()
>>> for key,val in DictionaryA.iteritems():
        lcl["Dict" + str(i)] = {key:val}
        i += 1

如前3个Dict变量所示:

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

如其他人所述,如果要将其放入函数中,应将其添加到EDOCX1[1]中:

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


您可以使用本地字典并将所有动态绑定作为项放入字典。然后,知道这样一个"动态变量"的名称,就可以使用该名称作为获取其值的键。