Python有时须要动态的创造Python代码,而后将其做为语句执行 或 做为表达式计算。python
exec用于执行存储在字符串中的Python代码。express
一、 语句与表达式的区别:表达式是 某事,语句是 作某事(即告诉计算机作什么)。安全
好比2*2是4,而print 2*2是打印4。上述两句代码在交互式解释器中执行的结果是同样的,是由于解释器老是把全部表达式的值打印出来而已。而在程序中编写相似2*2这样的表达式并不会打印显示什么,编写print 2*2则会打印4。函数
语句与表达式的区别在赋值时更明显,由于语句不是表达式,因此没有值。如在交互式解释器中输入 x=2则不会打印任何东西,马上出现新的提示符。虽然什么也没显现,可是有些东西已经发生变化如x的值如今变为3.这也是语句特性的通常定义:它们改变了事物。好比,赋值语句改变了变量,print语句改变了屏幕显示的内容。spa
二、 命名空间(做用域) 全局变量和局部变量code
除了全局做用域外,每一个函数会都会建立一个新的做用域。变量分为全局变量和局部变量,函数内的变量称为局部变量只在局部命名空间中起做用。对象
在函数内部读取全局变量通常来讲不是问题,直接访问便可。可是,若是局部变量名或者参数的名字与全局变量名相同的话,就不能直接访问了,由于全局变量被局部变量给屏蔽了。若是确实须要的话,可使用globals函数获取被屏蔽的全局变量值。(globals返回全局变量的字典,locals返回局部变量的值)。例如:有一个名为parameter的全局变量,那么在combine(parameter)函数内部访问全局变量时,由于与参数重名,必须使用globals()['parameter']获取。代码以下:作用域
def combine(parameter): print parameter+globals()['parameter'] #函数调用 parameter="hello" combine("berry")
上面讲的是再函数内部读取全局变量的方法,不包括修改。若是要在函数内部修改全局变量,须要告知修改的值是全局变量,由于在函数内部将值赋予一个变量那么变量自动成为局部变量。经过global关键字来告诉Python函数内一个须要修改的变量是一个全局变量。代码以下:
x=1 def change_global(n): global x x=x+1
三、执行字符串的语句 exec字符串
如输入exec "print 'hello'"会打印出hello。(注意:Python 3.0中,exec是一个函数不是一个语句了,所以使用exec('字符串语句')的方式来调用)。exec执行字符串语句存在安全风险,由于exec可能会干扰命名空间,即改变不该该变的变量。例如:input
从上面的例子能够看出,exec干扰了命名空间,改变了sqrt的值,使其不是一个函数而变成1了。因而可知,若是对exec不加限制就会存在安全风险。下面是改进措施。
措施:经过增长 in <scope>来实现,其中的<scope>是一个字典,该字典起到放置代码字符串命名空间的做用。这样exec执行的代码只会在<scope>表明的命名空间中起做用。如:
从上面代码中能够看到,exec语句在scope命名空间中执行,不会影响到如今命名空间的sqrt。scope虽然充当命名空间的做用,但实质还是一个字典,因此若是想知道scope命名空间中有多少变量时,可经过len(scope)得到,可经过scope.key()得到scope命名空间的全部变量。
四、eval 会计算字符串形式的Python表达式,并返回结果的值。
exec语句不会返回任何对象。而eval会返回表达式的值。下面的代码能够建立一个Python计算器:
#Python计算器 print eval(raw_input("Please input an arithmetic expression:"))
上面代码解释,上面代码中eval内部如今还不是字符串,首先执行raw_input()函数,raw_input()返回你输入的求值字符串,如今eval函数内部就是求值字符串了,就能够用eval进行字符串的求值了。如输入:4*5+6,那么raw_input就会返回“4*5+6”,eval求值后为26.
要注意上面代码与下面代码的区别:
print eval('raw_input("Please input an arithmetic expression:")')
在这个代码中,与Python计算器代码不一样的是,eval函数内直接就是字符串,那么直接对字符串求值,可是字符串中是raw_input表达式,raw_input表达式将用户的输入转换为字符串,因此若是输入4+5的话会返回"4+5"。注意:raw_input('xxxxx')是一个表达式,表达式的值就是用户输入。 可能疑惑的是代码:exec('raw_input("Please input an arithmetic expression:")')不会报错,由于ecec也能够用于表达式,只是什么效果也没达到而已(既不返回值,也没干事情)。
跟exec同样,eval也可使用命名空间。由于尽管表达式通常不会给变量从新赋值,可是表达式能够经过调用函数来达到给全局变量赋值的目的。例如执行下面代码后,全局变量x的值会被从新赋值为2:
x=1 def inc_x(): global x x=x+1 eval("inc_x()") print x
从上面的代码能够看出eval函数也是不安全的,必须使用命名空间。事实上,能够为eval提供两个命名空间,一个是全局的,另外一个是局部的。全局的必须是字典,局部的能够是任何形式的映射。
exec和eval的命名空间使用代码(命名空间能够不是空的字典,能够提早为命名空间提供一些值):
scope={} scope['x']=1 scope['y']=2 print eval('x+y',scope)
scope={} exec "x=2" in scope eval("x*x",scope)