原文: https://thepythonguru.com/python-builtin-functions/eval/
于 2020 年 1 月 7 日更新
eval()允许我们执行任意字符串作为 Python 代码。 它接受源字符串并返回一个对象。
其语法如下:
语法:
eval(expr, globals=None, locals=None)
| 参数 | 描述 |
|---|---|
expr(必填) |
expr可以是任何有效的 Python 表达式 |
globals(可选) |
执行源时要使用的全局名称空间。 它必须是字典。 如果未提供,则将使用当前的全局名称空间。 |
locals(可选) |
执行源时要使用的本地名称空间。 它可以是任何映射。 如果省略,则默认为globals字典。 |
如果同时省略globals和locals,则使用当前的全局和局部名称空间。
这是一个演示eval()如何工作的示例:
>>>>>> eval("5 == 5")True>>>>>>>>> eval("4 < 10")True>>>>>>>>> eval("8 + 4 - 2 * 3")6>>>>>>>>> eval("'py ' * 5")'py py py py py '>>>>>>>>> eval("10 ** 2")100>>>>>>>>> eval("'hello' + 'py'")'hellopy'>>>
试试看:
print(eval("5 == 5"))print(eval("4 < 10"))print(eval("8 + 4 - 2 * 3"))print(eval("'py ' * 5"))print(eval("10 ** 2"))print(eval("'hello' + 'py'"))
eval()不仅限于简单表达。 我们可以执行函数,调用方法,引用变量等。
>>>>>> eval("abs(-11)")11>>>>>>>>> eval('"hello".upper()')'HELLO'>>>>>>>>> import os>>>>>>>>> eval('os.getcwd()') # get current working directory'/home/thepythonguru'>>>>>>>>> x = 2>>>>>> eval("x+4") # x is referenced inside the expression6>>>
试一试:
print(eval("abs(-11)"))print(eval('"hello".upper()'))import os# get current working directoryprint(eval('os.getcwd()'))x = 2print(eval("x+4")) # x is referenced inside the expression
请注意,eval()仅适用于表达式。 尝试传递语句会导致SyntaxError。
>>>>>> eval('a=1') # assignment statementTraceback (most recent call last):File "<stdin>", line 1, in <module>File "<string>", line 1a=1^SyntaxError: invalid syntax>>>>>>>>> eval('import re') # import statementTraceback (most recent call last):File "<stdin>", line 1, in <module>File "<string>", line 1import re^SyntaxError: invalid syntax>>>
邪恶的eval()
您永远不要直接将不受信任的源传递给eval()。 由于恶意用户很容易对您的系统造成破坏。 例如,以下代码可以用于从系统中删除所有文件。
>>>eval('os.system("RM -RF /")') # command is deliberately capitalized>>>
如果os模块在您当前的全局范围内不可用,则以上代码将失败。 但是我们可以通过使用__import__()内置函数轻松地避免这种情况。
>>>>>> eval("__import__('os').system('RM -RF /')") # command is deliberately capitalized>>>
那么有什么方法可以使eval()安全吗?
指定命名空间
eval()可选地接受两个映射,作为要执行的表达式的全局和局部名称空间。 如果未提供映射,则将使用全局和局部名称空间的当前值。
这里有些例子:
示例 1:
>>>>>> globals = {... 'a': 10,... 'fruits': ['mangoes', 'peaches', 'bananas'],... }>>>>>>>>> locals = {}>>>>>>>>> eval("str(a) + ' ' + fruits[0]", globals, locals)'10 mangoes'>>>
示例 2:
>>>>>> eval('abs(-100)', {}, {})100>>>
即使我们已经将空字典作为全局和本地名称空间传递了,eval()仍可以访问内置函数(即__builtins__)。
>>>>>> dir(__builtins__)['ArithmeticError', 'AssertionError', 'AttributeError', 'BaseException', 'BlockingIOError', 'BrokenPipeError', 'BufferError', 'BytesWarning', 'ChildProcessError',......'property', 'quit', 'range', 'repr', 'reversed', 'round', 'set', 'setattr', 'slice', 'sorted', 'staticmethod', 'str', 'sum', 'super', 'tuple', 'type', 'vars', 'zip']>>>
要从全局名称空间中删除内置函数,请传递一个字典,该字典包含一个值为None的键__builtins__。
示例 3:
>>>>>> eval('abs(-100)', {'__builtins__':None}, {})Traceback (most recent call last):File "<stdin>", line 1, in <module>File "<string>", line 1, in <module>TypeError: 'NoneType' object is not subscriptable>>>
即使删除对内置函数的访问权限后,eval()仍然不安全。 考虑以下清单。
>>>>>> eval("5**98765432111123", {'__builtins__':None}, {})>>>
这个看似简单的外观表达式足以使您的 CPU 崩溃。
关键要点是仅将eval()与受信任的源一起使用。
