pin_drop当前位置:知识文库 ❯ 图文

Python eval exec函数

一、eval 与 exec 函数概述

eval()exec() 是 Python 中两个强大的内置函数,用于动态执行字符串形式的 Python 代码。它们允许程序在运行时动态生成和执行代码,提供了极高的灵活性。

  • 动态执行:在运行时执行字符串形式的 Python 代码

  • 灵活性高:可以根据程序逻辑动态生成代码

  • 需谨慎使用:存在安全风险,必须严格控制输入


二、eval 函数详解

基本语法

代码示例

eval(expression, globals=None, locals=None)

参数说明

参数 类型 说明 默认值
expression 字符串 要执行的 Python 表达式 必需
globals 字典 全局命名空间 None
locals 字典 局部命名空间 None

基本用法示例

代码示例

# 示例1:计算数学表达式
result = eval("2 + 3 * 4")
print(result)  # 输出: 14

# 示例2:使用变量
x = 10
result = eval("x * 2 + 5")
print(result)  # 输出: 25

# 示例3:动态创建列表
my_list = eval("[1, 2, 3, 4, 5]")
print(my_list)  # 输出: [1, 2, 3, 4, 5]

# 示例4:使用自定义命名空间
namespace = {'x': 10, 'y': 20}
result = eval("x + y", namespace)
print(result)  # 输出: 30

三、exec 函数详解

基本语法

代码示例

exec(object, globals=None, locals=None)

与 eval 的区别

exec() 可以执行任意 Python 代码(包括语句、函数定义、类定义等),而 eval() 只能执行单个表达式。exec() 返回 None,而 eval() 返回表达式的值。

基本用法示例

代码示例

# 示例1:执行多条语句
code = """
x = 10
y = 20
z = x + y
print(f"z = {z}")
"""
exec(code)  # 输出: z = 30

# 示例2:动态定义函数
func_code = """
def greet(name):
    return f"Hello, {name}!"
"""
exec(func_code)
print(greet("Alice"))  # 输出: Hello, Alice!

# 示例3:动态定义类
class_code = """
class Person:
    def __init__(self, name):
        self.name = name
    
    def say_hello(self):
        print(f"Hello, I'm {self.name}")
"""
exec(class_code)
p = Person("Bob")
p.say_hello()  # 输出: Hello, I'm Bob

# 示例4:使用自定义命名空间
namespace = {}
exec("x = 100", namespace)
print(namespace['x'])  # 输出: 100

四、eval 与 exec 对比

对比项 eval() exec()
执行内容 单个表达式 任意 Python 代码
返回值 表达式的值 None
支持语句 不支持 支持
典型用途 计算表达式、解析数据 动态生成代码、执行脚本
安全性 相对较低 风险更高

五、安全风险与防范

警告eval()exec() 可以执行任意代码,如果执行来自不可信来源的输入,可能导致严重的安全问题,包括数据泄露、系统被控制等。

危险示例

代码示例

# 危险!永远不要这样做
user_input = input("请输入表达式: ")
result = eval(user_input)  # 用户可能输入恶意代码

# 恶意输入示例:
# __import__('os').system('rm -rf /')  # 删除文件
# open('/etc/passwd').read()  # 读取敏感文件

安全实践

代码示例

# 安全实践1:限制可用函数
safe_dict = {'__builtins__': {}}  # 禁用所有内置函数
result = eval("2 + 3", safe_dict)
print(result)  # 输出: 5

# 安全实践2:只允许特定函数
import math
safe_dict = {
    '__builtins__': {},
    'math': math,
    'abs': abs,
    'max': max,
    'min': min
}
result = eval("math.sqrt(16)", safe_dict)
print(result)  # 输出: 4.0

# 安全实践3:使用 ast.literal_eval(推荐)
import ast
user_input = "[1, 2, 3, 4, 5]"
safe_list = ast.literal_eval(user_input)  # 只解析字面量,不执行代码
print(safe_list)  # 输出: [1, 2, 3, 4, 5]

六、实际应用场景

场景1:动态计算器

代码示例

def safe_calculator(expression):
    """安全的计算器,只允许数学运算"""
    allowed_names = {
        'abs': abs,
        'round': round,
        'max': max,
        'min': min,
        'sum': sum
    }
    
    try:
        # 检查是否包含危险字符
        dangerous = ['import', 'exec', 'eval', 'open', '__']
        if any(d in expression.lower() for d in dangerous):
            raise ValueError("不允许的操作")
        
        return eval(expression, {"__builtins__": {}}, allowed_names)
    except Exception as e:
        return f"错误: {e}"

# 使用示例
print(safe_calculator("2 + 3 * 4"))  # 输出: 14
print(safe_calculator("max(10, 20, 30)"))  # 输出: 30
print(safe_calculator("abs(-42)"))  # 输出: 42

场景2:动态配置加载

代码示例

# 从字符串加载配置
config_str = """
DEBUG = True
DATABASE = {
    'host': 'localhost',
    'port': 3306,
    'user': 'admin'
}
MAX_CONNECTIONS = 100
"""

config = {}
exec(config_str, config)

print(config['DEBUG'])  # 输出: True
print(config['DATABASE']['host'])  # 输出: localhost
print(config['MAX_CONNECTIONS'])  # 输出: 100

场景3:解析数据格式

代码示例

import ast

# 安全地解析 Python 字面量字符串
data_str = "{'name': 'Alice', 'age': 30, 'scores': [85, 92, 78]}"
data = ast.literal_eval(data_str)

print(data['name'])  # 输出: Alice
print(data['scores'])  # 输出: [85, 92, 78]

# 比 eval 更安全,不会执行代码
malicious = "__import__('os').system('ls')"
try:
    ast.literal_eval(malicious)
except ValueError as e:
    print(f"安全拦截: {e}")

七、注意事项

注意1:永远不要对用户输入直接使用 eval()exec(),除非你完全控制了输入内容并做了严格的安全检查。

注意2:对于解析 Python 字面量(列表、字典、元组等),优先使用 ast.literal_eval(),它只解析字面量,不执行代码,更安全。

注意3exec() 执行的代码可以修改当前命名空间,使用时要注意变量污染问题。建议使用独立的命名空间字典。


八、常见问题

常见问题

Q1: eval 和 exec 的性能如何?

由于需要解析和编译字符串,eval()exec() 的性能比直接执行代码慢。如果需要频繁执行相同代码,建议先编译:code = compile(expression, '<string>', 'eval'),然后重复使用。

Q2: 如何在 exec 中获取变量的值?

使用命名空间字典:namespace = {}; exec("x = 10", namespace); print(namespace['x'])。这样可以在执行后访问定义的变量。

Q3: ast.literal_eval 和 eval 有什么区别?

ast.literal_eval() 只解析 Python 字面量(字符串、数字、元组、列表、字典、布尔值和 None),不执行任何代码,因此是安全的。eval() 可以执行任意表达式,存在安全风险。

Q4: 有哪些替代 eval/exec 的方案?

对于数学计算,可以使用 operator 模块或第三方库如 numexpr。对于数据解析,使用 jsonast.literal_eval() 或专门的解析器。对于动态代码生成,考虑使用模板引擎或代码生成工具。


九、练习题

练习1

使用 eval() 计算字符串表达式 "2 ** 10 + 5 * 3",并打印结果。

练习2

使用 exec() 动态定义一个函数 calculate_area(radius),该函数计算并返回圆的面积(使用 math.pi)。

练习3

使用 ast.literal_eval() 安全地解析字符串 "{'name': 'Alice', 'scores': [85, 92, 78]}",并打印解析后的字典。

标签: eval函数 exec函数 动态执行 安全风险 Python高级

本文涉及AI创作

内容由AI创作,请仔细甄别

list快速访问

上一篇: Python enumerate函数 下一篇: Python内置函数format()

poll相关推荐