您好,登錄后才能下訂單哦!
這篇文章主要介紹“怎么使用Python eval函數”的相關知識,小編通過實際案例向大家展示操作過程,操作方法簡單快捷,實用性強,希望這篇“怎么使用Python eval函數”文章能幫助大家解決問題。
我們可以使用內置的 Python eval()[1] 從基于字符串或基于編譯代碼的輸入中動態地計算表達式。如果我們向 eval() 傳遞一個字符串,那么該函數會解析它,將其編譯為字節碼[2],并將其作為一個 Python 表達式進行計算。但是如果我們用一個編譯過的代碼對象調用 eval(),那么該函數只執行計算步驟,如果我們用相同的輸入多次調用 eval(),這就非常方便了。
Python的 eval() 的定義如下。
eval(expression[, globals[, locals]])
該函數需要一個第一個參數,稱為expression,它包含了需要計算的表達式。eval()還需要兩個可選參數。
globals
locals
在接下來的內容中,我們將學習這些參數是什么,以及 eval() 如何使用它們來即時計算Python 表達式。
注意: 我們也可以使用 exec()[3] 來動態地執行 Python 代碼。eval() 和 exec() 的主要區別是,eval() 只能執行或計算表達式,而 exec() 可以執行任何一段 Python代碼。
eval() 的第一個參數稱為 expression,它是一個必需的參數,用于保存函數的 基于字符串 或 基于編譯碼的 輸入。當調用 eval() 時,expression 的內容被作為 Python 表達式進行計算。下面是使用基于字符串的輸入的例子。
>>> eval("2 ** 8") 256 >>> eval("1024 + 1024") 2048 >>> eval("sum([8, 16, 32])") 56 >>> x = 100 >>> eval("x * 2") 200
當用一個字符串作為參數調用 eval() 時,該函數返回對輸入字符串進行計算的結果。默認情況下,eval()可以訪問全局變量名,如上例中的x。
為了計算一個基于字符串的表達式,Python 的 eval() 運行以下步驟。
解析表達式
將其編譯為字節碼
將其作為一個Python表達式進行計算
返回計算的結果
eval()的第一個參數 expression 強調了該函數只作用于表達式,并非復合語句[4]。Python 文檔對 expression 的定義如下。
expression
一段可以被計算為某種值的語法。換句話說,表達式是表達式元素的累積,如字面意義、名稱、屬性訪問、運算符或函數調用,它們都返回一個值。與許多其他語言相比,并非所有的語言結構都是表達式。也有一些語句不能作為表達式使用,如 while。此外賦值也是語句,不是表達式。
另一方面,Python statement 有如下定義。
statement
statement是一個套件(一個代碼 "塊")的一部分。statement要么是一個表達式,要么是帶有關鍵字的幾個結構體之一,如 if、while或for。
如果向eval()傳遞一個復合語句,那么會得到一個 SyntaxError。下面的例子是用eval()來執行一個if語句。
>>> x = 100 >>> eval("if x: print(x)") File "", line 1 if x: print(x) ^ SyntaxError: invalid syntax
上面報錯是因為 eval() 只接受表達式。任何其它語句,如 if、for、while、import、def 或 class,都會引發錯誤。
注意: for 循環是一個復合語句,但是 for 關鍵字也可以用在推導式中,此時它被認為是表達式。可以使用eval() 來計算推導式,即使它們使用了 for 關鍵字。
eval()也不允許進行賦值操作。
>>> eval("pi = 3.1416") File "", line 1 pi = 3.1416 ^ SyntaxError: invalid syntax
如果我們將一個賦值操作作為參數傳遞給 eval() ,那么會得到一個 SyntaxError。賦值操作是語句,而不是表達式,語句不允許與 eval() 一起使用。
當解析器不理解輸入的表達式時,也會得到一個 SyntaxError。在下面的例子中計算一個違反 Python 語法的表達式。
>>> # Incomplete expression >>> eval("5 + 7 *") File "", line 1 5 + 7 * ^ SyntaxError: unexpected EOF while parsing
所以,不能把一個違反 Python 語法的表達式傳給 eval() 。在上面的例子中,我們嘗試計算一個不完整的表達式 ("5 + 7 *") 時拋出一個 SyntaxError,因為分析器不理解表達式的語法。
我們也可以把已編譯的代碼對象傳遞給 eval() 。因此可以使用函數 compile()[7] ,一個內置函數,可以將輸入的字符串編譯成代碼對象[8] 或 AST 對象[9],這樣就可以用 eval() 來計算它。
如何使用compile()的細節超出了本文的范圍,但這里可以快速了解一下它的前三個必要參數。
source保存我們要編譯的源代碼。這個參數可以接受普通字符串、字節字符串[10]和AST對象。
filename給出讀取代碼的文件。如果我們要使用一個基于字符串的輸入,那么這個參數的值應該是"
mode指定了我們想得到哪種編譯后的代碼。如果我們想用eval()來處理編譯后的代碼,那么這個參數應該被設置為"eval"。
我們可以使用 compile() 向eval()提供代碼對象,而不是普通的字符串。
>>> # 算術運算 >>> code = compile("5 + 4", "", "eval") >>> eval(code) 9 >>> code = compile("(5 + 7) * 2", "", "eval") >>> eval(code) 24 >>> import math >>> # 一個球體的體積 >>> code = compile("4 / 3 * math.pi * math.pow(25, 3)", "", "eval") >>> eval(code) 65449.84694978735
如果我們使用 compile() 來編譯要傳遞給eval()的表達式,那么eval()會經過以下步驟。
計算編譯后的代碼
返回計算的結果
如果使用基于編譯碼的輸入調用 eval() ,那么該函數會執行計算步驟并立即返回結果。當需要多次計算同一個表達式時,這可能很方便。在這種情況下,最好預先編譯表達式,并在隨后調用 eval() 時重復使用產生的字節碼。
如果我們事先編譯了輸入表達式,那么連續調用eval()將運行得更快,因為我們不會重復解析和編譯的步驟。如果我們正在計算復雜的表達式,不需要的重復會導致高的CPU時間和過度的內存消耗。
eval() 的第二個參數 globals,可選的,字典類型,為 eval() 提供一個全局命名空間。通過 globals 告訴 eval() 在計算表達式時要使用哪些全局變量名。
全局變量名是所有那些在當前全局范圍或命名空間中可用的變量名。可以從代碼的任何地方訪問它們。
在字典中傳遞給 globals 的所有名字在執行時都可以提供給 eval() 。請看下面的例子,它展示了如何使用一個自定義的字典來為 eval() 提供一個全局命名空間。
>>> x = 100# 一個全局變量 >>> eval("x + 100", {"x": x}) 200 >>> y = 200# 另一個全局變量 >>> eval("x + y", {"x": x}) Traceback (most recent call last): File "", line 1, inFile "", line 1, inNameError: name 'y' is not defined
如果為 eval() 的 globals 參數提供一個自定義字典,那么 eval() 將只接受這些名字作為 globals。在這個自定義字典之外定義的任何全局變量名都不能從 eval() 內部訪問。這就是為什么當你試圖在上述代碼中訪問 y 時,Python 會引發一個 NameError。傳遞給 globals 的字典不包括 y。
可以通過在字典中列出名字來插入 globals,然后這些名字在求值過程中就會出現。例如,如果在 globals 中插入了 y,那么在上面的例子中對 "x + y" 的求值將如期進行。
>>> eval("x + y", {"x": x, "y": y}) 300
因為把 y 添加到了自定義 globals 字典中,所以成功計算 "x + y" 的值,得到的預期返回值 300。
我們也可以提供不存在于當前全局范圍的變量名。此時需要為每個名字提供一個具體的值。eval()在運行時將把這些變量名解釋為全局變量名。
>>> eval("x + y + z", {"x": x, "y": y, "z": 300}) 600 >>> z Traceback (most recent call last): File "", line 1, inNameError: name 'z' is not defined
盡管z沒有在當前的全局范圍內定義,但是這個變量在全局中的值是300,此時eval()可以訪問z,就像它是一個全局變量一樣。
globals 背后的機制是相當靈活的,可以向 globals 傳遞任何可見的變量(全局、局部、或者非局部)。還可以傳遞自定義的鍵值對,比如上面例子中的 "z": 300,那么eval() 將把它們全部作為全局變量處理。
關于 globals 中的注意事項,如果我們提供給它的自定義字典不包含鍵值 "__builtins__",那么在表達式被解析之前,對內置字典的引用將自動插入 "__builtins__" 下面。這可以確保 eval() 在計算表達式時可以完全訪問所有的 Python 內置變量名。
下面的例子表明,即使給 globals 提供了一個空的字典,對 eval() 的調用仍然可以訪問 Python 的內置變量名。
>>> eval("sum([2, 2, 2])", {}) 6 >>> eval("min([1, 2, 3])", {}) 1 >>> eval("pow(10, 2)", {}) 100
在上面的代碼中,我們向 globals 提供了一個空的字典 ({})。由于這個字典不包含一個叫做 "__builtins__" 的鍵,Python 會自動插入一個指向 builtins 中名字的引用。這樣,eval() 在解析表達式時就可以完全訪問所有 Python 的內置名字。
如果調用 eval() 而沒有將自定義字典傳遞給 globals ,那么參數將默認為在調用 eval()的環境中 globals() 返回的字典:
>>> x = 100#一個全局變量 >>> y = 200# 另一個全局變量 >>> eval("x + y")# 訪問兩個全局變量 300
當調用 eval() 而不提供 globals 參數時,該函數使用 globals() 返回的字典作為其全局命名空間來計算表達式。所以,在上面的例子中,我們可以自由地訪問 x 和 y,因為它們是包含在我們當前全局范圍內的全局變量。
Python 的 eval() 第三個參數 locals ,可選參數,字典類型。此時這個字典包含了 eval() 在計算表達式時作為局部變量名使用的變量。
局部變量名是那些我們在一個給定的函數內定義的名稱(變量、函數、類等等)。局部名稱只在封閉的函數內可見。我們在編寫函數時定義這些變量名。
因為 eval() 已經寫好了,所以不能在它的代碼或局部范圍內添加局部變量名。然而可以向 locals 傳遞一個字典,eval()會把這些名字當作本地名字。
>>> eval("x + 100", {}, {"x": 100}) 200 >>> eval("x + y", {}, {"x": 100}) Traceback (most recent call last): File "", line 1, inFile "", line 1, inNameError: name 'y' is not defined
第一個調用 eval() 的第二個字典保存了變量 x。這個變量被 eval() 解釋為一個局部變量。換句話說,它被看作是在 eval() 中定義的一個變量。
我們可以在表達式中使用 x,并且 eval() 可以訪問它。相反,如果使用y,那么會得到一個 NameError,因為y沒有定義在 globals 命名空間或 locals 命名空間。
和 globals 一樣,可以向 locals 傳遞任何可見的變量(全局、局部或非局部)。也可以傳遞自定義的鍵值對,比如 "x"。eval()將把它們全部作為局部變量處理。
注意,要給 locals 提供一個字典,首先需要給 globals 提供一個字典。不能在 eval() 中使用關鍵字參數。
>>> eval("x + 100", locals={"x": 100}) Traceback (most recent call last): File "", line 1, inTypeError: eval() takes no keyword arguments
如果在調用 eval() 時使用關鍵字參數,那么拋出一個 TypeError。這是因為 eval() 不接受關鍵字參數,所以在提供 locals 字典之前,需要先提供一個 globals 字典。
如果沒有給 locals 傳遞一個字典,那么它就默認為傳遞給 globals 的字典。這里有一個例子,給 globals 傳遞了一個空的字典,而 locals 沒有傳遞任何值。
>>> x = 100 >>> eval("x + 100", {}) Traceback (most recent call last): File "", line 1, inFile "", line 1, inNameError: name 'x' is not defined
鑒于沒有給 locals 提供一個自定義的字典,這個參數默認為傳遞給 globals 的字典。此時eval() 無法訪問 x,因為 globals 持有一個空的字典。
globals 和 locals 之間的主要實際區別是,如果"__builtins__"鍵不存在,Python 會自動插入 globals 中。無論我們是否為 globals 提供了一個自定義的字典,這都會發生。此外,如果我們給 locals 提供了一個自定義的字典,那么在執行 eval() 的過程中,這個字典將保持不變。
我們可以使用Python的eval()來計算任何一種Python表達式,但不包括Python語句,如基于關鍵字的復合語句或賦值語句。
當我們需要動態地計算表達式,而使用其它 Python 技術或工具會大大增加我們的開發時間和精力時,eval() 可以很方便。
在這一節中,我們將學習如何使用 Python 的 eval() 來計算布爾、數學和通用的 Python 表達式。
布爾表達式 是Python表達式,當解釋器對其進行計算時返回一個真值(True 或者 False)。它們通常用在if語句中,以檢查某些條件是否為真或假。由于布爾表達式不是復合語句,我們可以使用eval()來計算它們。
>>> x = 100 >>> y = 100 >>> eval("x != y") False >>> eval("x < 200 and y > 100") False >>> eval("x is y") True >>> eval("x in {50, 100, 150, 200}") True
我們可以用 eval() 來處理使用以下任何Python運算符的布爾表達式。
值比較運算符:< , > ,
<=,>=, ==, !=
邏輯(布爾)運算符:and,or,not
成員測試運算符:in,not in
身份運算符:is,is not
在所有情況下,該函數都會返回正在計算的表達式的真值。
我們思考,為什么我應該使用eval()而不是直接使用布爾表達式呢?假設需要實現一個條件語句,但我們想臨時改變條件。
>>> def func(a, b, condition): ... if eval(condition): ... return a + b ... return a - b ... >>> func(2, 4, "a > b") -2 >>> func(2, 4, "a < b") 6 >>> func(2, 2, "a is b") 4
在func()中,使用eval()來計算所提供的條件,并根據計算的結果返回a+b或a-b。在上面的例子中,只使用了幾個不同的條件,但還可以使用任何數量的其他條件,只要堅持使用我們在func()中定義的名稱a和b。
現在想象一下,如果不使用Python的eval(),我們將如何實現這樣的東西。那會花更少的代碼和時間嗎?不可能!
Python 的 eval() 的一個常見用例是對基于字符串的輸入進行 math 表達式的計算。例如,創建一個 Python 計算器,那么可以使用 eval() 來計算用戶的輸入并返回計算結果。
下面的例子演示了如何使用eval()與數學一起進行math運算。
>>> # Arithmetic operations >>> eval("5 + 7") 12 >>> eval("5 * 7") 35 >>> eval("5 ** 7") 78125 >>> eval("(5 + 7) / 2") 6.0 >>> import math >>> # 一個圓的面積 >>> eval("math.pi * pow(25, 2)") 1963.4954084936207 >>> # 球體的體積 >>> eval("4 / 3 * math.pi * math.pow(25, 3)") 65449.84694978735 >>> # 直角三角形的斜邊 >>> eval("math.sqrt(math.pow(10, 2) + math.pow(15, 2))") 18.027756377319946
當我們使用eval()來計算數學表達式時,我們可以傳入任何種類或復雜程度的表達式,eval()會解析它們,計算它們,如果一切正常,就會給我們預期結果。
前面我們已經學會了如何在布爾和 math 表達式中使用 eval() 。然而,我們可以在更復雜的 Python 表達式中使用 eval() ,這些表達式包括函數調用、對象創建、屬性訪問、列表推導式等等。
例如,可以調用一個內置函數或用標準或第三方模塊導入的函數。
>>> # 運行echo命令 >>> import subprocess >>> eval("subprocess.getoutput('echo Hello, World')") 'Hello, World' >>> # 啟動Firefox(如果有的話) >>> eval("subprocess.getoutput('firefox')") ''
在這個例子中,我們使用 Python 的 eval() 來執行一些系統命令。我們可以用這個功能做大量有用的事情。然而,eval()也會有一些嚴重的安全風險,比如允許一個惡意的用戶在我們的機器中運行系統命令或任何任意的代碼。
關于“怎么使用Python eval函數”的內容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業相關的知識,可以關注億速云行業資訊頻道,小編每天都會為大家更新不同的知識點。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。