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)