Python动态生成变量名和exec函数及eval函数的用法

最近在做编译原理课程设计,在实现基于DAG的局部优化算法时需要生成很多变量且变量需要以n1、n2、n3····n100这种形式命名作为DAG结点的编码,使用其他静态编译语言据我了解只能在代码中手动写出这100个变量名,但是查阅资料发现Python能够实现动态生成变量名而不像静态语言一样笨拙。

解决动态生成变量名的问题有几种方法,类似locals函数、exec函数。其中我选择的是exec函数,选择的原因是这两个函数起先我都不了解,于是尝试写一些demo学习使用它们,然而locals函数好像在我的应用场景中并不适用又或者是由于我的使用方法不对导致的无法得到想要的结果,诸多原因使得我最终选择了exec函数来动态生成变量名。

单独使用exec函数其实并不能动态生成变量名,与format函数加以配合才能达到该目的。

exec函数

这里首先介绍一下exec函数。
exec函数是Python的built-in函数(内置函数)。exec函数的实际作用动态执行Python代码。也就是说exec函数可以执行复杂的Python代码。

1
exec(source, globals=None, locals=None, /)

参数说明

  • source:必选参数,表示需要被指定的Python代码。它必须是字符串或者code对象。如果source是一个字符串,该字符串会先被解析为一组Python语句,然后执行。如果source是code对象,那么它只是被简单的执行。
  • globals:可选参数,表示全局命名空间(存放全局变量),如果被提供,则必须是一个字典对象。
  • locals:可选参数,表示局部命名空间(存放局部变量),如果被提供,可以是任何映射对象。如果参数被忽略,那么它将会取与globals相同的值。

如果globalslocals都被忽略,那么它们将取exec函数被调用环境下的全局命名空间和局部命名空间。

返回值exec函数的返回值永远为None

示例:

1
2
3
4
5
>>> i = 2
>>> j = 3
>>> exec("ans = i + j")
>>> print("Answer is: ", ans)
Answer is: 5

解释一下在上个例子中,ans变量并没有显式的定义,但仍然可以在print函数中调用。这是由于exec()语句执行了"ans = i + j"中的代码,定义了ans变量。

format函数

str.format函数是一种格式化字符串的函数,它增强了字符串格式化的功能。

基本语法是通过{}:来代替之前的%
format函数可以接受不限个参数,位置可以不按顺序。

示例:

1
2
3
4
>>>"{} {}".format("hello", "world")    # 不设置指定位置,按默认顺序
'hello world'
>>>"{1} {0} {1}".format("hello", "world") # 设置指定位置
'world hello world'

同样可以设置参数:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#!/usr/bin/python
# -*- coding: UTF-8 -*-

>>> print("网站名:{name}, 地址 {url}".format(name="菜鸟教程", url="www.runoob.com"))
>>> 网站名:菜鸟教程, 地址 www.runoob.com

# 通过字典设置参数
>>> site = {"name": "菜鸟教程", "url": "www.runoob.com"}
>>> print("网站名:{name}, 地址 {url}".format(**site))
>>> 网站名:菜鸟教程, 地址 www.runoob.com

# 通过列表索引设置参数
>>> my_list = ['菜鸟教程', 'www.runoob.com']
>>> print("网站名:{0[0]}, 地址 {0[1]}".format(my_list)) # "0" 是必须的
>>> 网站名:菜鸟教程, 地址 www.runoob.com

也可以向str.format()传入对象:

1
2
3
4
5
6
7
8
#!/usr/bin/python
# -*- coding: UTF-8 -*-

class AssignValue(object):
def __init__(self, value):
self.value = value
my_value = AssignValue(6)
print('value 为: {0.value}'.format(my_value)) # "0" 是可选的

输出结果为:

1
value 为: 6

菜鸟教程中还有一些数字格式化的具体教程,感兴趣的可以看一下。

动态生成变量名

将上述介绍的exec函数和format函数结合起来就能够做到动态生成变量名。

示例:

1
2
3
4
5
6
In [1]: for i in range(5):
...: exec('var{} = {}'.format(i, i))
...:

In [2]: print(var0, var1, var2, var3 ,var4)
0 1 2 3 4

eval函数

eval函数与exec函数有些相似但又有些不同exec,所以这里同时介绍eval函数,与exec函数对比记忆加深理解。
eval函数同样能够做到动态执行代码,但是它所能够执行的代码相比exec函数有特殊的限定。

eval函数的实际作用计算指定表达式的值。也就是说它要执行的Python代码只能是单个表达式(注意eval不支持任何形式的赋值操作),而不能是复杂的代码逻辑。

1
eval(source, globals=None, locals=None, /)

参数说明exec函数的一样。

返回值
如果source是一个code对象,且创建该code对象时,complie函数的mode参数是exec,那么eval函数的返回值是None;
否则,如果source是一个输出语句,如print(),则eval()的返回结果为None
否则,source表达式的结果就是eval()的返回值。

示例:

1
2
3
4
5
6
7
8
9
10
11
12
x = 10
def func():
y = 20 #局部变量y
a = eval("x+y")
print("a:",a) #x没有就调用全局变量
b = eval("x+y",{"x":1,"y":2}) #定义局部变量,优先调用
print("b:",b)
c = eval("x+y",{"x":1,"y":2},{"y":3,"z":4})
print("c:",c)
d = eval("print(x,y)")
print("d:",d) #对于变量d,因为print()函数不是一个计算表达式,因此没有返回值
func()

输出结果:

1
2
3
4
5
a: 30
b: 3
c: 4
10 20
d: None

eval函数和exec函数的区别

  1. eval函数只能计算单个表达式的值,而exec函数可以动态运行代码段;
  2. eval函数可以有返回值,而exec函数返回值永远为None

参考资料

Python动态变量名定义与调用 - Pyerlife - 博客园
python中的exec()、eval()以及complie() - 明王不动心 - 博客园
Python format 格式化函数 | 菜鸟教程