Python中的 eval是什麼?
在Python中,我們有許多内置方法,這些方法對于使Python成為所有人的便捷語言至關重要,而eval是其中一種。eval函數的文法如下:
eval(expression, globals, locals)
如上所示,eval函數采用三個參數:
- expression –需要一個字元串,該字元串将被解析并評估為Python表達式
- globals(可選)–一個字典,用于指定可用的全局方法和變量。
- locals(可選)-另一個字典,用于指定可用的本地方法和變量。
稍後将在本文中顯示對global(全局變量)s和locals(本地變量)的使用。
eval在Python中做什麼?eval函數解析expression參數并将其評估為python表達式。換句話說,我們可以說這個函數解析了傳遞給它的表達式并在程式中運作python expression(code)。
為了評估基于字元串的表達式,Python的eval函數運作以下步驟:
- 解析表達式
- 編譯成位元組碼
- 将其評估為Python表達式
- 傳回評估結果
這意味着當我們将任何python表達式作為“字元串”傳遞給eval函數時,它會評估該表達式并将結果傳回為整數或浮點數。以下是一些簡單的示例,它們将使您更加清楚。
這是在Python中使用eval将字元串轉換為整數,複數或浮點數的簡單方法:
<code> num =“ 23”
float_num =“ 53.332”
complex_num =“ 2 + 3j”
str1 =“ Not number”
print(eval(num),type(eval(num)))
print(eval(float_num),type( eval(float_num)))
print(eval(complex_num),type(eval(complex_num)))
print(eval(str1),type(eval(str1)))</ code>
OUTPUT:
23
53.332
(2+3j)
Traceback (most recent call last):
File "main.py", line 8, in
print(eval(str1),type(eval(str1)))
File "", line 1
Not number
^
SyntaxError: unexpected EOF while parsing
如您所見,eval函數能夠識别字元串中的表達式并将其轉換為相應的類型。但是,當我們僅傳遞字元和字母時,它傳回了一個錯誤。這應該清楚eval的實際作用。
這裡有更多的例子,其中我們不僅僅涉及類型轉換,實際上我們看到了eval函數評估字元串中的表達式。
我們還可以使用eval求解數學表達式:
<code> expr =“(2+(3 * 2))/ 2”
print(eval(expr))</ code>
OUTPUT:
4.0
我們甚至可以在字元串中使用變量名,Python還将對它們進行評估,如下所示
<code>num=10
expr="(2+(3*2))/2 + num"
print(eval(expr))</code>
OUTPUT:
14.0
我們還可以在字元串内部使用内置函數,如下所示:
<code>print(eval("sum([8, 16, 34])"))</code>
OUTPUT:
58
為了更好地了解eval函數,讓我們看看如果将表達式用兩個字元串括起來,它将如何響應,如下所示:
<code>#string in another string
expr="'2+3'"
print(eval(expr))
print(eval(eval(expr)))</code>
OUTPUT:
2+3
5
是以,第一個eval函數隻是傳回字元串中的表達式,但是在另一個eval函數中使用eval時,我們得到了表達式的答案。
如何在python中使用eval ?
在上一節中,我們已經了解了如何使用eval函數,但是在這裡,我們将了解eval函數的其他參數如何影響其工作。是以,Python中的eval 還有兩個參數,即viz-globals和locals。
全局變量是目前全局範圍或命名空間中可用的對象。您可以從代碼中的任何位置通路它們。
在執行時,傳遞給字典中全局變量的所有對象将對eval()可用。請檢視以下示例,該示例顯示了如何使用自定義詞典為eval函數提供全局名稱空間:
<code>num1 = 100 # A global variable
print(eval("num1 + 100", {"num1": num1}))
num2 = 200 # Another global variable
print(eval("num1 + num2", {"num1": num1,"num2": num2}))
print(eval("num1 + num2", {"num1": num1}))</code>
OUTPUT:
200
300
Traceback (most recent call last):
File "main.py", line 5, in
print(eval("num1 + num2", {"num1": num1}))
File "", line 1, in
NameError: name 'num2' is not defined
如您在上面的示例中看到的,首先eval隻能通路num1和num2,但是當我從globals字典中删除num2時,它抛出了一個錯誤,因為它現在無法識别num2。
但是,為什麼在我甚至沒有将值傳遞給globals參數的上述示例中都沒有發生這種錯誤?
事實證明,當您在不提供globals參數的情況下調用eval函數時,該函數将使用globals()函數傳回的字典作為其全局命名空間來評估表達式。
是以,在上面的示例中,我們可以自由通路所有變量,因為它們是目前全局範圍中包含的全局變量。
現在,如果将空字典傳遞給全局變量會發生什麼,讓我們看看:
<code>a=2
print(eval("sum([2, 2, 2])", {}))
print(eval("sum([a, 2, 2])", {}))</code>
OUTPUT:
6
Traceback (most recent call last):
File "main.py", line 3, in
print(eval("sum([a, 2, 2])", {}))
File "", line 1, in
NameError: name 'a' is not defined
是以,eval函數可以成功識别函數和,但無法識别對象“ a”,是以傳回錯誤。
當我們向全局變量提供自定義詞典時,它包含鍵“ __builtins__”的值,但如果不包含該值,則在解析表達式之前,将自動在“ __builtins__”下插入對内置字典的引用。這樣可以確定eval()函數在評估表達式時将完全通路所有Python的内置名稱。這說明了在上面的示例中,如何通過eval識别函數和。
現在讓我們看看什麼是局部變量以及它們如何擴充eval函數的功能。與全局變量不同,局部對象在函數内部聲明,不能在函數外部通路。
類似地,locals參數采用一個字典,在字典中我們添加了一些對象,而eval()函數将這些對象視為本地對象。請看下面的例子:
<code>print(eval("sum([a, 2, 2])",{}, {"a":2}))
print(a)</code>
OUTPUT:
6
Traceback (most recent call last):
File "main.py", line 2, in
print(a)
NameError: name 'a' is not defined
請注意,要向本地人提供字典,您首先需要向全局人提供字典。不能将關鍵字參數與eval()一起使用
這似乎令人困惑,但是在下面的示例中,我同時使用了globals和locals參數,您将看到它們如何影響結果。
<code>print(eval("abs(-1)"))
#By keeping __builtins__":None,eval will recognise no in-buiilt function
print(eval('abs(-1)',{"__builtins__":None}))</code>
OUTPUT:
1
Traceback (most recent call last):
File "main.py", line 1, in
print(eval('abs(-1)',{"__builtins__":None}))
File "", line 1, in
TypeError: 'NoneType' object is not subscriptable
現在,我們希望該函數在eval函數中起作用,是以将其添加到本地字典中。現在,eval函數可以識别abs函數,而不能識别任何其他函數。
<code>print(eval('abs(-1)',{"__builtins__":None},{"abs":abs}))</code>
OUTPUT:
1
全局變量和局部變量之間的主要實際差別是,如果該密鑰尚不存在,Python會自動将“ __builtins__”鍵插入全局變量。無論是否為全局變量提供自定義詞典,都會發生這種情況。另一方面,如果向本地人提供自定義詞典,則在執行eval函數期間該詞典将保持不變。
評估的局限性
Python中的eval()很有用,但也有重要的安全隐患。eval函數被認為是不安全的,因為它允許您或其他使用者動态執行任意Python代碼。那對我們有什麼影響?
假設您正在伺服器上運作的應用程式中要求使用者輸入。現在,如果您在輸入上使用eval函數,則使用者可以通路伺服器本身。使用者可以像這樣傳遞一些可疑的代碼:
<code> __ import __('subprocess')。getoutput('rm –rf *')
</ code>
上面的代碼将删除應用程式目前目錄中的所有檔案,這肯定會影響我們。
是以,最好避免使用eval函數,但是如果仍然要使用eval函數,我們可以借助globals和locals參數來限制其功能。正如我們在上一節中看到的那樣,我們限制eval函數,使其隻能使用python的abs函數。
例如,假設我有一個應用程式,可以在給定數字或所有給定數字的總和中找到最小值。像這樣使用eval的最友善方法
<code> print(eval(input()))
#input:sum([1,3,4])
#output:8
#input:min([1,3,4])
#output:1 </ code>
但是,這是一種不好的程式設計方式。我們無法控制使用者的輸入内容,是以我們可以利用globals和locals參數,使得eval不能識别sum()和min()以外的函數。這肯定會和上面的代碼做同樣的事情,但是要安全得多。
<code> print(eval(input(),{“ __ builtins __”:None},{“ min”:min,“ sum”:sum}))</ code>
希望大家可以通過本文了解到Python中的 eval函數,如果您想了解更多有關python案例實操,建議閱讀“ Python經典80案例實操
”