pin_drop当前位置:知识文库 ❯ 图文
Python eval exec函数
目录
一、eval 与 exec 函数概述
eval() 和 exec() 是 Python 中两个强大的内置函数,用于动态执行字符串形式的 Python 代码。它们允许程序在运行时动态生成和执行代码,提供了极高的灵活性。
-
动态执行:在运行时执行字符串形式的 Python 代码
-
灵活性高:可以根据程序逻辑动态生成代码
-
需谨慎使用:存在安全风险,必须严格控制输入
二、eval 函数详解
基本语法
代码示例
eval(expression, 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()可以执行任意代码,如果执行来自不可信来源的输入,可能导致严重的安全问题,包括数据泄露、系统被控制等。
危险示例
代码示例
# 危险!永远不要这样做
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(),它只解析字面量,不执行代码,更安全。
注意3:
exec()执行的代码可以修改当前命名空间,使用时要注意变量污染问题。建议使用独立的命名空间字典。
八、常见问题
常见问题
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。对于数据解析,使用 json、ast.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]}",并打印解析后的字典。
本文涉及AI创作
内容由AI创作,请仔细甄别