python中執行字符串形式的語句和字符串形式的表達式方法(即exec和eval方法)


前陣子一直在思考一個問題,就是如何讓用戶在圖形界面上輸入的代碼(輸入的代碼為字符串),成為代碼的一部分而運行起來,恰逢看python爬蟲的視頻教程的時候,看到了使用eval函數,之后查找到了該文章,解決了我思考的問題。  

@文章來源:https://my.oschina.net/duhaizhang/blog/66048

Python有時需要動態的創造Python代碼,然后將其作為語句執行  或  作為表達式計算。

              exec用於執行存儲在字符串中的Python代碼。

             1、 語句與表達式的區別:表達式是 某事,語句是 做某事(即告訴計算機做什么)。

              比如2*2是4,而print 2*2是打印4。上述兩句代碼在交互式解釋器中執行的結果是一樣的,是因為解釋器總是把所有表達式的值打印出來而已。而在程序中編寫類似2*2這樣的表達式並不會打印顯示什么,編寫print 2*2則會打印4。

              語句與表達式的區別在賦值時更明顯,因為語句不是表達式,所以沒有值。如在交互式解釋器中輸入 x=2則不會打印任何東西,立刻出現新的提示符。雖然什么也沒顯現,但是有些東西已經發生變化如x的值現在變為3.這也是語句特性的一般定義:它們改變了事物。比如,賦值語句改變了變量,print語句改變了屏幕顯示的內容。

             2、 命名空間(作用域) 全局變量和局部變量

             除了全局作用域外,每個函數會都會創建一個新的作用域。變量分為全局變量和局部變量,函數內的變量稱為局部變量只在局部命名空間中起作用。

            在函數內部讀取全局變量一般來說不是問題,直接訪問即可。但是,如果局部變量名或者參數的名字與全局變量名相同的話,就不能直接訪問了,因為全局變量被局部變量給屏蔽了。如果確實需要的話,可以使用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

              3、執行字符串的語句  exec

               如輸入exec "print  'hello'"會打印出hello。(注意:Python 3.0中,exec是一個函數不是一個語句了,因此使用exec('字符串語句')的方式來調用)。exec執行字符串語句存在安全風險,因為exec可能會干擾命名空間,即改變不應該變的變量。例如:

                   從上面的例子可以看出,exec干擾了命名空間,改變了sqrt的值,使其不是一個函數而變成1了。由此可見,如果對exec不加限制就會存在安全風險。下面是改進措施。

               措施:通過增加 in <scope>來實現,其中的<scope>是一個字典,該字典起到放置代碼字符串命名空間的作用。這樣exec執行的代碼只會在<scope>代表的命名空間中起作用。如:

                 從上面代碼中可以看到,exec語句在scope命名空間中執行,不會影響到現在命名空間的sqrt。scope雖然充當命名空間的作用,但實質仍是一個字典,所以如果想知道scope命名空間中有多少變量時,可通過len(scope)獲得,可通過scope.key()獲得scope命名空間的所有變量。

                4、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)


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM