动态变量名赋值
在使用 tkinter 时需要动态生成变量,如动态生成 var1…var10 变量。
使用 exec 动态赋值
exec 在 python3 中是内置函数,它支持 python 代码的动态执行。
for i in range(6):
exec('var{} = {}'.format(i,i))
print(var0,var1,var2,var3,var4,var5)
运行结果如下:
0,1,2,3,4,5
利用命名空间动态赋值
在 Python 的命名空间中,将变量名与值存储在字典中,可以通过 locals(),globals() 函数分别获取局部命名空间和全局命名空间。
names = locals()
for i in range(5):
names['n' + str(i)] = i
print(n0,n1,n2,n3,n4)
运行结果如下:
0 1 2 3 4
在类中使用动态变量
Python 的类对象的属性储存在的 dict 中。dict 是一个词典,键为属性名,值对应属性的值。
class Test_class(object):
def __init__(self):
names = self.__dict__
for i in range(5):
names['n' + str(i)] = i
t = Test_class()
print(t.__dict__)
print(t.n0,t.n1,t.n2,t.n3,t.n4)
运行结果如下:
{'n0': 0, 'n1': 1, 'n2': 2, 'n3': 3, 'n4': 4}
0 1 2 3 4
利用 exec 函数
同样地,可以使用 exec 调用变量
for i in range(5):
exec('var{} = {}'.format(i, i))
for i in range(5):
exec('print(var{},end=" ")'.format(i))
运行结果如下:
0 1 2 3 4
利用命名空间
因为命令空间的 locals() 与 globals() 均会返回一个字典,利用字典的 get 方法获取变量的值。
for i in range(5):
exec('var{} = {}'.format(i, i))
names = locals()
for i in range(5):
print(names.get('var' + str(i)),end=' ')
运行结果如下:
0 1 2 3 4
你想在使用范围内执行某个代码片段,并且希望在执行后所有的结果都不可见。
>>> a = 14
>>> exec('b = a + 1')
>>> print(b)
15
在一个函数中执行同样的代码:(会发生错误)
>>> def test():
... a = 14
... exec('b = a + 1')
... print(b)
...
>>> test()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 4, in test
NameError: global name 'b' is not defined
>>>
可以看出,最后抛出了一个NameError异常,就跟在 exec() 语句从没执行过一样。要是你想在后面的计算中使用到 exec() 执行结果的话就会有问题了。
为了修正这样的错误,你需要在调用 exec() 之前使用 locals() 函数来得到一个局部变量字典。
之后你就能从局部字典中获取修改过后的变量值了。例如:
>>> def test():
... a = 13
... loc = locals()
... exec('b = a + 1')
... b = loc['b']
... print(b)
...
>>> test()
14
>>>
注意点:
默认情况下,exec() 会在调用者局部和全局范围内执行代码。然而,在函数里面,传递给 exec() 的局部范围是拷贝实际局部变量组成的一个字典。
因此,如果 exec() 如果执行了修改操作,这种修改后的结果对实际局部变量值是没有影响的。
>>> def test1():
... x = 0
... exec('x += 1')
... print(x)
...
>>> test1()
0
上面代码里,当你调用 locals() 获取局部变量时,你获得的是传递给 exec() 的局部变量的一个拷贝。
通过在代码执行后审查这个字典的值,那就能获取修改后的值了。
>>> def test2():
... x = 0
... loc = locals()
... print('before:', loc)
... exec('x += 1')
... print('after:', loc)
... print('x =', x)
...
>>> test2()
before: {'x': 0}
after: {'loc': {...}, 'x': 1}
x = 0
>>>
仔细观察最后一步的输出,除非你将 loc 中被修改后的值手动赋值给x,否则x变量值是不会变的。
在使用 locals() 的时候,你需要注意操作顺序。每次它被调用的时候,locals() 会获取局部变量值中的值并覆盖字典中相应的变量。
>>> def test3():
... x = 0
... loc = locals()
... print(loc)
... exec('x += 1')
... print(loc)
... locals()
... print(loc)
...
>>> test3()
{'x': 0}
{'loc': {...}, 'x': 1}
{'loc': {...}, 'x': 0}
>>>
注意最后一次调用 locals() 的时候x的值是如何被覆盖掉的。
作为 locals() 的一个替代方案,你可以使用你自己的字典,并将它传递给 exec() 。例如:
>>> def test4():
... a = 13
... loc = { 'a' : a }
... glb = { }
... exec('b = a + 1', glb, loc)
... b = loc['b']
... print(b)
...
>>> test4()
14
>>>
大部分情况下,这种方式是使用 exec() 的最佳实践。你只需要保证全局和局部字典在后面代码访问时已经被初始化。