利用Python+eval函數(shù)構(gòu)建數(shù)學(xué)表達(dá)式計(jì)算器
Python 中的函數(shù)eval()?是一個(gè)非常有用的工具,在前期,我們一起學(xué)習(xí)過(guò)該函數(shù)點(diǎn)擊查看:Python eval 函數(shù)動(dòng)態(tài)地計(jì)算數(shù)學(xué)表達(dá)式?。盡管如此,我們?cè)谑褂弥?,還需要考慮到該函數(shù)的一些重要的安全問(wèn)題。在本文中,云朵君將和大家一起學(xué)習(xí) eval() 如何工作,以及如何在 Python 程序中安全有效地使用它。
eval() 的安全問(wèn)題
本節(jié)主要學(xué)習(xí) eval() 如何使我們的代碼不安全,以及如何規(guī)避相關(guān)的安全風(fēng)險(xiǎn)。
eval() 函數(shù)的安全問(wèn)題在于它允許你(或你的用戶)動(dòng)態(tài)地執(zhí)行任意的Python代碼。
通常情況下,會(huì)存在正在讀(或?qū)懀┑拇a不是我們要執(zhí)行的代碼的情況。如果我們需要使用eval()來(lái)計(jì)算來(lái)自用戶或任何其他外部來(lái)源的輸入,此時(shí)將無(wú)法確定哪些代碼將被執(zhí)行,這將是一個(gè)非常嚴(yán)重的安全漏洞,極易收到黑客的攻擊。
一般情況下,我們并不建議使用 eval()。但如果非要使用該函數(shù),需要記住根據(jù)經(jīng)驗(yàn)法則:永遠(yuǎn)不要 用 未經(jīng)信任的輸入 來(lái)使用該函數(shù)。這條規(guī)則的重點(diǎn)在于要弄清楚我們可以信任哪些類型的輸入。
舉個(gè)例子說(shuō)明,隨意使用eval()?會(huì)使我們寫的代碼漏洞百出。假設(shè)你想建立一個(gè)在線服務(wù)來(lái)計(jì)算任意的Python數(shù)學(xué)表達(dá)式:用戶自定義表達(dá)式,然后點(diǎn)擊運(yùn)行?按鈕。應(yīng)用程序app獲得用戶的輸入并將其傳遞給eval()進(jìn)行計(jì)算。
這個(gè)應(yīng)用程序app將在我們的個(gè)人服務(wù)器上運(yùn)行,而那些服務(wù)器內(nèi)具有重要文件,如果你在一個(gè)Linux 操作系統(tǒng)運(yùn)行命令,并且該進(jìn)程有合法權(quán)限,那么惡意的用戶可以輸入危險(xiǎn)的字符串而損害服務(wù)器,比如下面這個(gè)命令。
"__import__('subprocess').getoutput('rm –rf *')"
上述代碼將刪除程序當(dāng)前目錄中的所有文件。這簡(jiǎn)直太可怕了!
注意: __import__()?是一個(gè)內(nèi)置函數(shù),它接收一個(gè)字符串形式的模塊名稱,并返回一個(gè)模塊對(duì)象的引用。__import__()? 是一個(gè)函數(shù),它與導(dǎo)入語(yǔ)句完全不同。我們不能使用 eval() 來(lái)計(jì)算一個(gè)導(dǎo)入語(yǔ)句。
當(dāng)輸入不受信任時(shí),并沒有完全有效的方法來(lái)避免eval()?函數(shù)帶來(lái)的安全風(fēng)險(xiǎn)。其實(shí)我們可以通過(guò)限制eval()的執(zhí)行環(huán)境來(lái)減少風(fēng)險(xiǎn)。在下面的內(nèi)容中,我們學(xué)習(xí)一些規(guī)避風(fēng)險(xiǎn)的技巧。
限制globals和locals
可以通過(guò)向 globals 和 locals 參數(shù)傳遞自定義字典來(lái)限制 eval()? 的執(zhí)行環(huán)境。例如,可以給這兩個(gè)參數(shù)傳遞空的字典,以防止eval()訪問(wèn)調(diào)用者當(dāng)前范圍或命名空間中的變量名。
# 避免訪問(wèn)調(diào)用者當(dāng)前范圍內(nèi)的名字 >>> x = 100 >>> eval("x * 5", {}, {}) Traceback (most recent call last): File "<stdin>", line 1, in <module> File "<string>", line 1, in <module> NameError: name 'x' is not defined
如果給 globals 和 locals 傳遞了空的字典({}?),那么eval()?在計(jì)算字符串 "x * 5 "? 時(shí),在它的全局名字空間和局部名字空間都找不到名字x?。因此,eval()將拋出一個(gè)NameError。
然而,像這樣限制 globals 和 locals 參數(shù)并不能消除與使用 Python 的 eval() 有關(guān)的所有安全風(fēng)險(xiǎn),因?yàn)槿匀豢梢栽L問(wèn)所有 Python 的內(nèi)置變量名。
限制內(nèi)置名稱的使用
函數(shù) eval()? 會(huì)在解析 expression 之前自動(dòng)將 builtins? 內(nèi)置模塊字典的引用插入到 globals 中。使用內(nèi)置函數(shù) __import__() 來(lái)訪問(wèn)標(biāo)準(zhǔn)庫(kù)和在系統(tǒng)上安裝的任何第三方模塊。這還容易被惡意用戶利用。
下面的例子表明,即使在限制了 globals 和 locals 之后,我們也可以使用任何內(nèi)置函數(shù)和任何標(biāo)準(zhǔn)模塊,如 math 或 subprocess。
>>> eval("sum([5, 5, 5])", {}, {}) 15 >>> eval("__import__('math').sqrt(25)", {}, {}) 5.0 >>> eval("__import__('subprocess').getoutput('echo Hello, World')", {}, {}) 'Hello, World'
我們可以使用 __import__() 來(lái)導(dǎo)入任何標(biāo)準(zhǔn)或第三方模塊,如導(dǎo)入 math 和 subprocess 。因此 可以訪問(wèn)在 math、subprocess 或任何其他模塊中定義的任何函數(shù)或類?,F(xiàn)在想象一下,一個(gè)惡意的用戶可以使用 subprocess 或標(biāo)準(zhǔn)庫(kù)中任何其他強(qiáng)大的模塊對(duì)系統(tǒng)做什么,那就有點(diǎn)恐怖了。
為了減少這種風(fēng)險(xiǎn),可以通過(guò)覆蓋 globals 中的 "__builtins__?" 鍵來(lái)限制對(duì) Python 內(nèi)置函數(shù)的訪問(wèn)。通常建議使用一個(gè)包含鍵值對(duì) "__builtins__:{}" 的自定義字典。
>>> eval("__import__('math').sqrt(25)", {"__builtins__": {}}, {}) Traceback (most recent call last): File "<stdin>", line 1, in <module> File "<string>", line 1, in <module> NameError: name '__import__' is not defined
如果我們將一個(gè)包含鍵值對(duì) "__builtins__: {}?" 的字典傳遞給 globals,那么 eval()? 就不能直接訪問(wèn) Python 的內(nèi)置函數(shù),比如 __import__()。
然而這種方法仍然無(wú)法使得 eval() 完全規(guī)避風(fēng)險(xiǎn)。
限制輸入中的名稱
即使可以使用自定義的 globals? 和 locals? 字典來(lái)限制 eval()?的執(zhí)行環(huán)境,這個(gè)函數(shù)仍然會(huì)被攻擊。例如可以使用像""、"[]"、"{}"或"() "?來(lái)訪問(wèn)類object以及一些特殊屬性。
>>> "".__class__.__base__ <class 'object'> >>> [].__class__.__base__ <class 'object'> >>> {}.__class__.__base__ <class 'object'> >>> ().__class__.__base__ <class 'object'>
一旦訪問(wèn)了 object,可以使用特殊的方法 `.__subclasses__()`來(lái)訪問(wèn)所有繼承于 object 的類。下面是它的工作原理。
>>> for sub_class in ().__class__.__base__.__subclasses__(): ... print(sub_class.__name__) ... type weakref weakcallableproxy weakproxy int ...
這段代碼將打印出一個(gè)大類列表。其中一些類的功能非常強(qiáng)大,因此也是一個(gè)重要的安全漏洞,而且我們無(wú)法通過(guò)簡(jiǎn)單地限制 eval() 的避免該漏洞。
>>> input_string = """[ ... c for c in ().__class__.__base__.__subclasses__() ... if c.__name__ == "range" ... ][0](10 "0")""" >>> list(eval(input_string, {"__builtins__": {}}, {})) [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
上面代碼中的列表推導(dǎo)式對(duì)繼承自 object? 的類進(jìn)行過(guò)濾,返回一個(gè)包含 range? 類的 list?。第一個(gè)索引([0]?)返回類的范圍。一旦獲得了對(duì) range? 的訪問(wèn)權(quán),就調(diào)用它來(lái)生成一個(gè) range? 對(duì)象。然后在 range? 對(duì)象上調(diào)用 list(),從而生成一個(gè)包含十個(gè)整數(shù)的列表。
在這個(gè)例子中,用 range? 來(lái)說(shuō)明 eval()? 函數(shù)中的一個(gè)安全漏洞?,F(xiàn)在想象一下,如果你的系統(tǒng)暴露了像 subprocess.Popen 這樣的類,一個(gè)惡意的用戶可以做什么?
我們或許可以通過(guò)限制輸入中的名字的使用,從而解決這個(gè)漏洞。該技術(shù)涉及以下步驟。
- 創(chuàng)建一個(gè)包含你想用eval()使用的名字的字典。
- 在eval? 模式下使用compile() 將輸入字符串編譯為字節(jié)碼。
- 檢查字節(jié)碼對(duì)象上的.co_names,以確保它只包含允許的名字。
- 如果用戶試圖輸入一個(gè)不允許的名字,會(huì)引發(fā)一個(gè)`NameError`。
看看下面這個(gè)函數(shù),我們?cè)谄渲袑?shí)現(xiàn)了所有這些步驟。
>>> def eval_expression(input_string): ... # Step 1 ... allowed_names = {"sum": sum} ... # Step 2 ... code = compile(input_string, "<string>", "eval") ... # Step 3 ... for name in code.co_names: ... if name not in allowed_names: ... # Step 4 ... raise NameError(f"Use of {name} not allowed") ... return eval(code, {"__builtins__": {}}, allowed_names)
eval_expression()? 函數(shù)可以在 eval()? 中使用的名字限制為字典 allowed_names? 中的那些名字。而該函數(shù)使用了 .co_names,它是代碼對(duì)象的一個(gè)屬性,返回一個(gè)包含代碼對(duì)象中的名字的元組。
下面的例子顯示了eval_expression() 在實(shí)踐中是如何工作的。
>>> eval_expression("3 + 4 * 5 + 25 / 2") 35.5 >>> eval_expression("sum([1, 2, 3])") 6 >>> eval_expression("len([1, 2, 3])") Traceback (most recent call last): File "<stdin>", line 1, in <module> File "<stdin>", line 10, in eval_expression NameError: Use of len not allowed >>> eval_expression("pow(10, 2)") Traceback (most recent call last): File "<stdin>", line 1, in <module> File "<stdin>", line 10, in eval_expression NameError: Use of pow not allowed
如果調(diào)用 eval_expression()? 來(lái)計(jì)算算術(shù)運(yùn)算,或者使用包含允許的變量名的表達(dá)式,那么將會(huì)正常運(yùn)行并得到預(yù)期的結(jié)果,否則會(huì)拋出一個(gè)`NameError`。上面的例子中,我們僅允許輸入的唯一名字是sum()?,而不允許其他算術(shù)運(yùn)算名稱如len()和pow(),所以當(dāng)使用它們時(shí),該函數(shù)會(huì)產(chǎn)生一個(gè)`NameError`。
如果完全不允許使用名字,那么可以把 eval_expression() 改寫:
>>> def eval_expression(input_string): ... code = compile(input_string, "<string>", "eval") ... if code.co_names: ... raise NameError(f"Use of names not allowed") ... return eval(code, {"__builtins__": {}}, {}) ... >>> eval_expression("3 + 4 * 5 + 25 / 2") 35.5 >>> eval_expression("sum([1, 2, 3])") Traceback (most recent call last): File "<stdin>", line 1, in <module> File "<stdin>", line 4, in eval_expression NameError: Use of names not allowed
現(xiàn)在函數(shù)不允許在輸入字符串中出現(xiàn)任何變量名。需要檢查.co_names?中的變量名,一旦發(fā)現(xiàn)就引發(fā) NameError。否則計(jì)算 input_string? 并返回計(jì)算的結(jié)果。此時(shí)也使用一個(gè)空的字典來(lái)限制locals。
我們可以使用這種技術(shù)來(lái)盡量減少eval()的安全問(wèn)題,并加強(qiáng)安全盔甲,防止惡意攻擊。
將輸入限制為只有字?jǐn)?shù)
函數(shù)eval()的一個(gè)常見用例是計(jì)算包含標(biāo)準(zhǔn)Python字面符號(hào)的字符串,并將其變成具體的對(duì)象。
標(biāo)準(zhǔn)庫(kù)提供了一個(gè)叫做 literal_eval() 的函數(shù),可以幫助實(shí)現(xiàn)這個(gè)目標(biāo)。雖然這個(gè)函數(shù)不支持運(yùn)算符,但它支持 list, tuples, numbers, strings等等。
>>> from ast import literal_eval >>> # 計(jì)算字面意義 >>> literal_eval("15.02") 15.02 >>> literal_eval("[1, 15]") [1, 15] >>> literal_eval("(1, 15)") (1, 15) >>> literal_eval("{'one': 1, 'two': 2}") {'one': 1, 'two': 2} >>> # 試圖計(jì)算一個(gè)表達(dá)式 >>> literal_eval("sum([1, 15]) + 5 + 8 * 2") Traceback (most recent call last): ... ValueError: malformed node or string: <_ast.BinOp object at 0x7faedecd7668>
注意,literal_eval()?只作用于標(biāo)準(zhǔn)類型的字詞。它不支持使用運(yùn)算符或變量名。如果向 literal_eval()? 傳遞一個(gè)表達(dá)式,會(huì)得到一個(gè) ValueError。這個(gè)函數(shù)還可以將與使用eval()有關(guān)的安全風(fēng)險(xiǎn)降到最低。
使用eval()與input()函數(shù)
在 Python 3.x 中,內(nèi)置函數(shù) input() 讀取命令行上的用戶輸入,去掉尾部的換行,轉(zhuǎn)換為字符串,并將結(jié)果返回給調(diào)用者。由于 input()? 的輸出結(jié)果是一個(gè)字符串,可以把它傳遞給 eval() 并作為一個(gè) Python 表達(dá)式來(lái)計(jì)算它。
>>> eval(input("Enter a math expression: ")) Enter a math expression: 15 * 2 30 >>> eval(input("Enter a math expression: ")) Enter a math expression: 5 + 8 13
我們可以將函數(shù) eval()? 包裹在函數(shù) input()? 中,實(shí)現(xiàn)自動(dòng)計(jì)算用戶的輸入的功能。一個(gè)常見用例模擬 Python 2.x 中 input()? 的行為,input() 將用戶的輸入作為一個(gè) Python 表達(dá)式來(lái)計(jì)算,并返回結(jié)果。
因?yàn)樗婕鞍踩珕?wèn)題,因此在 Python 2.x 中的 input() 的這種行為在 Python 3.x 中被改變了。
構(gòu)建一個(gè)數(shù)學(xué)表達(dá)式計(jì)算器
到目前為止,我們已經(jīng)了解了函數(shù) eval()? 是如何工作的以及如何在實(shí)踐中使用它。此外還了解到 eval()? 具有重要的安全漏洞,盡量在代碼中避免使用 eval()?,然而在某些情況下,eval()? 可以為我們節(jié)省大量的時(shí)間和精力。因此,學(xué)會(huì)合理使用 eval() 函數(shù)還是蠻重要的。
在本節(jié)中,將編寫一個(gè)應(yīng)用程序來(lái)動(dòng)態(tài)地計(jì)算數(shù)學(xué)表達(dá)式。首先不使用eval()來(lái)解決這個(gè)問(wèn)題,那么需要通過(guò)以下步驟:
- 解析輸入的表達(dá)式。
- 將表達(dá)式的組成部分變?yōu)镻ython對(duì)象(數(shù)字、運(yùn)算符、函數(shù)等等)。
- 將所有的東西合并成一個(gè)表達(dá)式。
- 確認(rèn)該表達(dá)式在Python中是有效的。
- 計(jì)算最終表達(dá)式并返回結(jié)果。
考慮到 Python 可以處理和計(jì)算的各種表達(dá)式非常耗時(shí)。其實(shí)我們可以使用 eval() 來(lái)解決這個(gè)問(wèn)題,而且通過(guò)上文我們已經(jīng)學(xué)會(huì)了幾種技術(shù)來(lái)規(guī)避相關(guān)的安全風(fēng)險(xiǎn)。
首先創(chuàng)建一個(gè)新的Python腳本,名為mathrepl.py,然后添加以下代碼。
import math __version__ = "1.0" ALLOWED_NAMES = { k: v for k, v in math.__dict__.items() if not k.startswith("__") } PS1 = "mr>>" WELCOME = f""" MathREPL {__version__}, your Python math expressions evaluator! Enter a valid math expression after the prompt "{PS1}". Type "help" for more information. Type "quit" or "exit" to exit. """ USAGE = f""" Usage: Build math expressions using numeric values and operators. Use any of the following functions and constants: {', '.join(ALLOWED_NAMES.keys())} """
在這段代碼中,我們首先導(dǎo)入 math 模塊。這個(gè)模塊使用預(yù)定義的函數(shù)和常數(shù)進(jìn)行數(shù)學(xué)運(yùn)算。常量 ALLOWED_NAMES? 保存了一個(gè)包含數(shù)學(xué)中非特變量名的字典。這樣就可以用 eval() 來(lái)使用它們。
我們還定義了另外三個(gè)字符串常量。將使用它們作為腳本的用戶界面,并根據(jù)需要打印到屏幕上。
現(xiàn)在準(zhǔn)備編寫核心功能,首先編寫一個(gè)函數(shù),接收數(shù)學(xué)表達(dá)式作為輸入,并返回其結(jié)果。此外還需要寫一個(gè)叫做 evaluate() 的函數(shù),如下所示。
def evaluate(expression): """Evaluate a math expression.""" # 編譯表達(dá)式 code = compile(expression, "<string>", "eval") # 驗(yàn)證允許名稱 for name in code.co_names: if name not in ALLOWED_NAMES: raise NameError(f"The use of '{name}' is not allowed") return eval(code, {"__builtins__": {}}, ALLOWED_NAMES)
以下是該功能的工作原理。
- 定義了evaluate(),該函數(shù)將字符串表達(dá)式作為參數(shù),并返回一個(gè)浮點(diǎn)數(shù),代表將字符串作為數(shù)學(xué)表達(dá)式進(jìn)行計(jì)算的結(jié)果。
- 使用compile()將輸入的字符串表達(dá)式變成編譯的Python代碼。如果用戶輸入了一個(gè)無(wú)效的表達(dá)式,編譯操作將引發(fā)一個(gè) SyntaxError。
- 使用一個(gè)for循環(huán),檢查表達(dá)式中包含的名字,并確認(rèn)它們可以在最終表達(dá)式中使用。如果用戶提供的名字不在允許的名字列表中,那么會(huì)引發(fā)一個(gè)NameError。
- 執(zhí)行數(shù)學(xué)表達(dá)式的實(shí)際計(jì)算。注意將自定義的字典傳遞給了globals和locals。ALLOWED_NAMES保存了數(shù)學(xué)中定義的函數(shù)和常量。
注意: 由于這個(gè)應(yīng)用程序使用了 math 中定義的函數(shù),需要注意,當(dāng)我們用一個(gè)無(wú)效的輸入值調(diào)用這些函數(shù)時(shí),其中一些函數(shù)將拋出 ValueError 異常。
例如,math.sqrt(-10)? 會(huì)引發(fā)一個(gè)異常,因?yàn)?10的平方根是未定義的。我們會(huì)在稍后的代碼中看到如何捕捉該異常。
為 globals 和 locals 參數(shù)使用自定義值,加上名稱檢查,可以將與使用eval()有關(guān)的安全風(fēng)險(xiǎn)降到最低。
當(dāng)在 main() 中編寫其代碼時(shí),數(shù)學(xué)表達(dá)式計(jì)算器就完成了。在這個(gè)函數(shù)中,定義程序的主循環(huán),結(jié)束讀取和計(jì)算用戶在命令行中輸入的表達(dá)式的循環(huán)。
在這個(gè)例子中,應(yīng)用程序?qū)ⅲ?/p>
- 向用戶打印一條歡迎信息
- 顯示一個(gè)提示,準(zhǔn)備讀取用戶的輸入
- 提供獲取使用說(shuō)明和終止應(yīng)用程序的選項(xiàng)
- 讀取用戶的數(shù)學(xué)表達(dá)式
- 計(jì)算用戶的數(shù)學(xué)表達(dá)式
- 將計(jì)算的結(jié)果打印到屏幕上
def main(): """Main loop: Read and evaluate user's input.""" print(WELCOME) while True: # 讀取用戶的輸入 try: expression = input(f"{PS1} ") except (KeyboardInterrupt, EOFError): raise SystemExit() # 處理特殊命令 if expression.lower() == "help": print(USAGE) continue if expression.lower() in {"quit", "exit"}: raise SystemExit() # 對(duì)表達(dá)式進(jìn)行計(jì)算并處理錯(cuò)誤 try: result = evaluate(expression) except SyntaxError: # 如果用戶輸入了一個(gè)無(wú)效的表達(dá)式 print("Invalid input expression syntax") continue except (NameError, ValueError) as err: # 如果用戶試圖使用一個(gè)不允許的名字 # 對(duì)于一個(gè)給定的數(shù)學(xué)函數(shù)來(lái)說(shuō)是一個(gè)無(wú)效的值 print(err) continue # 如果沒有發(fā)生錯(cuò)誤,則打印結(jié)果 print(f"The result is: {result}") if __name__ == "__main__": main()
在main()?中,首先打印WELCOME消息。然后在一個(gè)try語(yǔ)句中讀取用戶的輸入,以捕獲鍵盤中斷和 EOFError。如果這些異常發(fā)生,就終止應(yīng)用程序。
如果用戶輸入幫助選項(xiàng),那么應(yīng)用程序就會(huì)顯示使用指南。同樣地,如果用戶輸入quit或exit,那么應(yīng)用程序就會(huì)終止。
最后,使用evaluate()?來(lái)計(jì)算用戶的數(shù)學(xué)表達(dá)式,然后將結(jié)果打印到屏幕上。值得注意的是,對(duì) evaluate() 的調(diào)用會(huì)引發(fā)以下異常。
- SyntaxError:語(yǔ)法錯(cuò)誤,當(dāng)用戶輸入一個(gè)不符合Python語(yǔ)法的表達(dá)式時(shí),就會(huì)發(fā)生這種情況。
- NameError:當(dāng)用戶試圖使用一個(gè)不允許的名稱(函數(shù)、類或?qū)傩裕r(shí),就會(huì)發(fā)生這種情況。
- ValueError:當(dāng)用戶試圖使用一個(gè)不允許的值作為數(shù)學(xué)中某個(gè)函數(shù)的輸入時(shí),就會(huì)發(fā)生這種情況。
注意,在main()中,捕捉了所有已知異常,并相應(yīng)地打印信息給用戶。這將使用戶能夠?qū)彶楸磉_(dá)式,修復(fù)問(wèn)題,并再次運(yùn)行程序。
現(xiàn)在已經(jīng)使用函數(shù) eval() 在大約七十行的代碼中建立了一個(gè)數(shù)學(xué)表達(dá)式計(jì)算器。要運(yùn)行這個(gè)程序,打開我們的系統(tǒng)命令行,輸入以下命令。
$ python3 mathrepl.py
這個(gè)命令將啟動(dòng)數(shù)學(xué)表達(dá)式計(jì)算器的命令行界面(CLI),會(huì)在屏幕上看到類似這樣的東西。
MathREPL 1.0, your Python math expressions evaluator! Enter a valid math expression after the prompt "mr>>". Type "help" for more information. Type "quit" or "exit" to exit. mr>>
現(xiàn)在我們可以輸入并計(jì)算任何數(shù)學(xué)表達(dá)式。例如,輸入以下表達(dá)式。
mr>> 25 * 2 The result is: 50 mr>> sqrt(25) The result is: 5.0 mr>> pi The result is: 3.141592653589793
如果輸入了一個(gè)有效的數(shù)學(xué)表達(dá)式,那么應(yīng)用程序就會(huì)對(duì)其進(jìn)行計(jì)算,并將結(jié)果打印到屏幕上。如果表達(dá)式有任何問(wèn)題,那么應(yīng)用程序會(huì)告訴我們。
r>> 5 * (25 + 4 Invalid input expression syntax mr>> sum([1, 2, 3, 4, 5]) The use of 'sum' is not allowed mr>> sqrt(-15) math domain error mr>> factorial(-15) factorial() not defined for negative values
在第一個(gè)示例中,漏掉了右括號(hào),因此收到一條消息,告訴我們語(yǔ)法不正確。然后調(diào)用 sum()? ,這會(huì)得到一個(gè)解釋性的異常消息。最后,使用無(wú)效的輸入值調(diào)用“math”函數(shù),應(yīng)用程序?qū)⑸梢粭l消息來(lái)識(shí)別輸入中的問(wèn)題。
總結(jié)
你可以使用Python的 eval() 從基于字符串或基于代碼的輸入中計(jì)算Python 表達(dá)式。當(dāng)我們動(dòng)態(tài)地計(jì)算Python表達(dá)式,并希望避免從頭創(chuàng)建自己的表達(dá)式求值器的麻煩時(shí),這個(gè)內(nèi)置函數(shù)可能很有用。
在本文中,我們已經(jīng)學(xué)習(xí)了 eval() 是如何工作的,以及如何安全有效地使用它來(lái)計(jì)算任意Python表達(dá)式。
使用Python的eval()來(lái)動(dòng)態(tài)計(jì)算基本的Python表達(dá)式。
使用eval()運(yùn)行更復(fù)雜的語(yǔ)句,如函數(shù)調(diào)用、對(duì)象創(chuàng)建和屬性訪問(wèn)。
最大限度地減少與使用Python的eval()有關(guān)的安全風(fēng)險(xiǎn)。
到此這篇關(guān)于利用Python+eval函數(shù)構(gòu)建數(shù)學(xué)表達(dá)式計(jì)算器的文章就介紹到這了,更多相關(guān)Python eval數(shù)學(xué)表達(dá)式內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
對(duì)Python實(shí)現(xiàn)簡(jiǎn)單的API接口實(shí)例講解
今天小編就為大家分享一篇對(duì)Python實(shí)現(xiàn)簡(jiǎn)單的API接口實(shí)例講解,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2018-12-12Python實(shí)現(xiàn)獲取某天是某個(gè)月中的第幾周
這篇文章主要介紹了Python實(shí)現(xiàn)獲取某天是某個(gè)月中的第幾周,本文代碼實(shí)現(xiàn)獲取指定的某天是某個(gè)月中的第幾周、周一作為一周的開始,需要的朋友可以參考下2015-02-02Python 解碼Base64 得到碼流格式文本實(shí)例
今天小編就為大家分享一篇Python 解碼Base64 得到碼流格式文本實(shí)例,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2020-01-01python高級(jí)內(nèi)置函數(shù)用法實(shí)例
在本篇文章里小編給大家整理的是一篇關(guān)于python高級(jí)內(nèi)置函數(shù)用法實(shí)例內(nèi)容,有興趣的朋友們可以學(xué)參考下。2021-09-09Python 同級(jí)目錄(兄弟目錄)調(diào)用方式
這篇文章主要介紹了Python 同級(jí)目錄(兄弟目錄)調(diào)用方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-02-02