3.6 使用exec和eval执行求值字符串
使用过JavaScript语言的朋友应该对其中的eval函数印象深刻。没有接触过的朋友请看我详细的介绍。eval函数可以将一个字符串当做JavaScript代码执行,也就是说,可以动态执行JavaScript代码。其实Python语言也有类似的功能,这就是exec函数。
在终端中按照以下代码依次输入:
>>> exec('i = 20')
>>> exec('print(i)')
20
>>> print(i * i)
400
从上面代码可以看到,调用了两次exec函数,该函数的参数是字符串类型的值,在本例中是两句合法的Python语句。exec函数成功的执行了这两条语句,并输出了最终的结果。从这点可以看出,exec函数不仅可以执行Python代码,还可以共享上下文,而且通过exec函数执行Python代码,与直接通过Python解释器执行是完全一样的。上下文都是共享的,所以最后用print函数输出i * i 的结果是400。
不过在使用exec函数执行Python代码的时候需要注意,尽可能不要让用户可以在全局作用域下执行Python代码,否则可能会与命名空间冲突。
例如下面代码:
>>> from random import randint
>>> randint(1,20)
15
>>> exec('randint = 30')
>>> randint(1,20)
Traceback (most recent call lase):
File "",line 1,in
TypeError:'int' object is not callable
在上面的代码中,导入了random模块中的randint函数,该函数用于返回一个随机整数。但在用exec函数执行的Python代码中,将randint函数作为一个变量赋值了,因此在后面的代码中就无法使用randint函数随机生成整数了。为了解决这个问题,可以为exec函数指定第2个参数值,用来表示防止exec函数执行的Python代码的作用域(字典)。
>>> from random import randint
>>> randint(1,20)
9
>>> scope == {}
>>> exec('randint = 30',scope)
>>> randint(1,20)
19
>>> scope.keys()
dict_keys(['_builtins_','randint'])
在上面代码中,为exec函数指定了第2个参数(一个字典类型的变量)。这时randint = 30 设置的randint变量实际上属于scope,而不是全局的,所以与randint函数并没有冲突。使用scope.keys函数查看scope中的key,会看到randint。
下面我们在讲exec函数中的第3个参数,用于为exec函数要指定的Python代码传递参数值。
>>> a = 20
>>> args = {'a':20,'b':30}
>>> scope = {}
>>> exec('print(a + b)',scope,args)
50
在上面的代码中,exec函数要执行的代码是print(a + b),这的a和b是两个变量,不过这两个变量的定义代码并不是由exec函数执行的,而是在调用exec函数前通过args定义的,args是一个字典,其中有两个key:a和b,它们的值分别是20和30。exec会根据字典的key对应要执行代码中的同名变量,如果匹配,就会将字典中相应的值传入要执行的代码。
在Python语言中还有另外一个函数eval。这个函数余exec函数类似,只是eval是用于执行表达式的,并返回结果值。而exec函数并不会返回任何值,该函数只是执行Python代码。可以利用eval函数的特性实现一个可以计算表达式的计算器。另外,eval也可以像exec函数一样,指定scope和为要执行的代码传递参数值。
>>> eval('1 + 2 - 4')
-1
>>> eval('2 * (6 - 4)')
4
>>> scope={'x':20}
>>> args={'y':40}
>>> eval('x + y',scope,args)
60
[例 3.10] 本例将利用exec函数实现一个Python控制台。可以在控制台中输入任意多条Python语句,然后按Enter键执行前面输入的所有Python语句。
scope = {}
codes = "" #用于保存输入的所有代码
print(">>>",end=" ") #输出Python控制台提示符
while True:
code = input("") #输入的代码
if code == "": #如果输入的是空串,会执行以前输入的所有Python代码
exec(codes,scope) #执行以前输入的所有Python代码
codes = "" #重置codes变量,以便重新输入Python代码
print(">>>",end=" ") #继续输出Python控制台提示符
continue #忽略后面的代码
codes += code + "\n" #将输入的每一行代码首尾相连,中间换行
输出结果:
>>> a = 10
b = 20
c = 30
print(a * (b + c))
500
>>>
这个Python控制台程序与执行Python命令进入的控制台程序类似,只是并不想Python命令进入的Python控制台一样输入一条语句就执行一条语句,而是输入完了,在一起执行。
回过头来我们解释一下什么是字典,字典是集合的一种,通过关键字(key)查找值(value),在Python语言中用一对花括号({})定义字典变量,key和value之间用冒号(:)分隔,多个key-value之间用逗号分隔。关于字典,以后会更加详细的介绍。