ZetCode

Python eval 函数

上次修改时间:2025 年 4 月 11 日

本综合指南探讨了 Python 的 eval 函数,该函数动态评估表达式。我们将涵盖语法、安全风险、实际应用以及安全评估的最佳实践。

基本定义

eval 函数将字符串解析并评估为 Python 表达式。 它返回评估表达式的结果。 该函数可以选择采用全局变量和局部变量字典来确定变量范围。

主要特征:将字符串评估为代码,支持仅表达式评估(而非语句),并且对不受信任的输入可能很危险。 始终在使用 eval 在生产代码之前验证输入。

基本表达式评估

以下是一个简单的用法,展示了 eval 如何评估字符串中的数学表达式和基本 Python 操作。

basic_eval.py
# Simple arithmetic
result = eval("2 + 3 * 4")
print(result)  # 14

# With variables
x = 10
y = 5
print(eval("x + y"))  # 15

# Boolean expressions
print(eval("10 > 5 and 3 < 4"))  # True

此示例显示 eval 评估不同的表达式类型。 当变量在作用域中时,它可以处理算术、变量引用和布尔逻辑。

该函数返回评估表达式的结果,从而允许基于字符串输入的动态代码执行。 请注意,只有表达式有效,而不是像赋值或循环这样的语句。

使用 globals 和 locals

eval 可以接受可选的 globals 和 locals 字典来控制变量访问。 此示例演示了受限评估。

scope_eval.py
# Restricted namespace
safe_dict = {'x': 5, 'y': 10}
result = eval("x + y", safe_dict)
print(result)  # 15

# Missing variables
try:
    eval("z + 5", safe_dict)
except NameError as e:
    print(f"Error: {e}")  # name 'z' is not defined

# Built-ins disabled
try:
    eval("len('test')", {'__builtins__': None})
except NameError as e:
    print(f"Error: {e}")  # name 'len' is not defined

这展示了如何在 eval 中控制变量访问。 第一个示例使用自定义字典来存储变量。 其他示例演示了对未定义变量和内置函数的受限访问。

在评估不受信任的输入时,限制 globals 和 locals 对于安全至关重要。 尽可能限制可用的命名空间。

数学表达式评估器

这个实际示例创建了一个安全的计算器,该计算器评估数学表达式,同时防止危险操作。

calculator.py
import math

def safe_eval(expr):
    allowed_names = {
        k: v for k, v in math.__dict__.items() 
        if not k.startswith('_')
    }
    allowed_names.update({'abs': abs, 'round': round})
    
    try:
        return eval(expr, {'__builtins__': None}, allowed_names)
    except (SyntaxError, NameError, TypeError) as e:
        return f"Error: {e}"

print(safe_eval("sqrt(16) + log10(100)"))  # 6.0
print(safe_eval("__import__('os').system('ls')"))  # Error: name '__import__' is not defined

此计算器仅允许特定的数学函数,同时阻止危险操作。 它演示了如何创建安全的评估环境。

该函数将数学运算列入白名单并捕获潜在的错误。 第二个示例展示了它如何防止恶意代码执行尝试。

模板字符串评估

eval 可用于简单的模板评估,其中需要将表达式动态插入到字符串中。

template_eval.py
def eval_template(template, context):
    try:
        return eval(f'f"""{template}"""', {'__builtins__': None}, context)
    except Exception as e:
        return f"Template error: {e}"

user = {'name': 'Alice', 'age': 30}
template = "Hello {name}, you are {age} years old. Next year: {age+1}"
print(eval_template(template, user))
# Hello Alice, you are 30 years old. Next year: 31

此示例展示了如何将 eval 与 f 字符串一起使用以进行动态模板评估。 上下文字典提供变量。

虽然功能强大,但此方法应仅用于受信任的模板。 对于不受信任的输入,请考虑使用字符串模板等替代方案。

带表达式的 JSON 配置

这个高级示例演示了评估包含简单表达式的 JSON 配置,这对于动态设置很有用。

config_eval.py
import json
import math

def eval_config(config_str):
    config = json.loads(config_str)
    context = {
        'math': math,
        'env': {'width': 1024, 'height': 768}
    }
    
    if 'formula' in config:
        try:
            config['value'] = eval(
                config['formula'],
                {'__builtins__': None},
                context
            )
        except Exception as e:
            config['error'] = str(e)
    
    return config

config_json = '''{
    "name": "display",
    "formula": "math.sqrt(env['width']**2 + env['height']**2)"
}'''

result = eval_config(config_json)
print(result['value'])  # 1280.0 (diagonal of 1024x768 display)

这展示了如何安全地评估 JSON 配置中的表达式。 该公式使用勾股定理计算对角线长度。

该示例通过限制内置函数,同时允许通过上下文字典使用特定的数学运算和环境变量来维护安全性。

最佳实践

资料来源

作者

我叫 Jan Bodnar,是一位充满热情的程序员,拥有丰富的编程经验。 自 2007 年以来,我一直在撰写编程文章。 迄今为止,我撰写了 1,400 多篇文章和 8 本电子书。 我拥有超过十年的编程教学经验。

列出所有 Python 教程