Python eval()
函数
原文: 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 expression
6
>>>
试一试:
print(eval("abs(-11)"))
print(eval('"hello".upper()'))
import os
# get current working directory
print(eval('os.getcwd()'))
x = 2
print(eval("x+4")) # x is referenced inside the expression
请注意,eval()
仅适用于表达式。 尝试传递语句会导致SyntaxError
。
>>>
>>> eval('a=1') # assignment statement
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<string>", line 1
a=1
^
SyntaxError: invalid syntax
>>>
>>>
>>> eval('import re') # import statement
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<string>", line 1
import 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()
与受信任的源一起使用。