Python中eval与ast.literal_eval解析字符串转列表的区别

小鹿同学
小鹿同学
发布时间:2026-10-26| 4人浏览过
原创

在 Python 开发中,若需要将形如 "[1, 2, 3]" 或 "['apple', 'banana']" 的文本形式转换为真正的列表对象,开发者通常会想到内置函数 eval() 与标准库 ast 模块提供的 ast.literal_eval()。两者的核心结论非常明确:在将字符串转换为列表的常规开发中,应当始终使用 ast.literal_eval(),坚决杜绝使用 eval()。eval() 会无限制地执行传入文本中的任何 Python 代码,存在致命的代码注入隐患;而 ast.literal_eval() 仅通过受限的抽象语法树解析原生字面量,从底层设计上排除了执行任何函数与恶意指令的可能。

一、底层解析机制与架构原理的本质区别

理解两者差异的根源,在于它们处理字符串时走过了完全不同的底层执行管道。

eval() 是 Python 解释器的全功能代码执行接口。当传入字符串时,解释器会将其视作一段完整的 Python 表达式源码,依次执行完整的词法分析、语法分析并编译成虚拟机字节码,最后放入当前的作用域上下文(全局与局部命名空间)中由 Python 虚拟机执行。这意味着传入的字符串拥有调用任何内置模块、创建对象乃至操作操作系统的完整权限。

与此相对,ast.literal_eval() 是标准库中专为“安全反序列化”设计的字面量解析函数。它同样会调用底层语法分析生成抽象语法树(AST),但在树遍历阶段,它只接受严格的白名单节点类型(如 Constant、List、Tuple、Dict、Set 等基本字面量)。一旦在语法树中检测到诸如函数调用(Call)、变量名引用(Name)或未允许的操作符,便会立即终止解析并抛出异常,根本不会生成和运行虚拟机字节码。

Python中eval与ast.literal_eval解析字符串转列表的区别
图 1:eval() 与 ast.literal_eval() 底层执行管道与安全过滤差异

二、安全性实测:为什么不能用 eval 解析外部数据

在Web开发或接口数据对接场景中,待解析的字符串往往来自客户端请求、文件上传或外部系统。如果采用 eval() 解析字符串转列表,外部攻击者只需稍微修改请求内容,即可获得服务器的完全控制权。

考虑以下典型恶意输入示例:

# 外部传入看似列表、实则包含攻击载荷的字符串
payload = "__import__('os').system('cat /etc/passwd')"

# 如果使用 eval 处理:
# eval 会直接调用底层系统命令打印系统用户列表,严重破坏服务器边界
eval(payload)

# 如果使用 ast.literal_eval 处理:
import ast
try:
    ast.literal_eval(payload)
except ValueError as e:
    print(f"成功拦截恶意输入: {e}")
# 输出:成功拦截恶意输入: malformed node or string

在面对带有函数调用、属性访问或操作符重载的代码时,ast.literal_eval() 仅将其视作不合规的 AST 节点并抛出错误,完全不会触发任何系统调用或函数执行。因此,它能够安全地处理来自不可信信道的任何文本。

Python中eval与ast.literal_eval解析字符串转列表的区别
图 2:四类典型输入场景下 eval 与 ast.literal_eval 的行为对比矩阵

三、实操规范:使用 ast.literal_eval 解析字符串的标准步骤

在实际生产代码中,直接调用 ast.literal_eval() 仍然可能因为格式错误或非预期类型导致程序崩溃。实现可靠的“字符串转列表”操作,需要遵循包含清洗、防御性捕获和类型断言的完整步骤。

标准操作步骤与代码实现

  1. 输入类型与空值前置检查:确保输入是合法的非空字符串,并进行去除两端空白字符(strip())的处理。
  2. 双重异常捕获:在 try...except 块中调用 ast.literal_eval(),必须同时捕获 ValueError(非字面量或不支持的表达式)与 SyntaxError(括号未闭合或语法破损)。
  3. 类型断言与兜底处理:即使 ast.literal_eval() 成功解析,返回结果也可能是字典(如 "{'a': 1}")或元组(如 "(1, 2)")。若明确需要列表,必须使用 isinstance(result, list) 进行二次校验。
import ast
from typing import List, Any, Optional

def parse_string_to_list(raw_str: str) -> Optional[List[Any]]:
    """
    安全地将字符串解析为 Python 列表。
    若解析失败或解析结果不是列表,返回 None 或抛出业务异常。
    """
    if not isinstance(raw_str, str):
        return None
    
    clean_str = raw_str.strip()
    if not clean_str:
        return []
    
    try:
        parsed_obj = ast.literal_eval(clean_str)
    except (ValueError, SyntaxError):
        # 捕获格式错误或非法表达式,阻止程序崩溃
        return None
    
    # 验证解析产物是否为预期的列表类型
    if isinstance(parsed_obj, list):
        return parsed_obj
    
    return None

# 测试示例
print(parse_string_to_list("[1, 2, 'data', False]"))  # 示例输出: [1, 2, 'data', False]
print(parse_string_to_list("(1, 2, 3)"))               # 示例输出: None (防范元组误认)
print(parse_string_to_list("恶意代码注入"))             # 示例输出: None (安全捕获)
Python中eval与ast.literal_eval解析字符串转列表的区别
图 3:ast.literal_eval 处理字符串转列表的标准执行流程与防范要点

四、语法支持边界与工具选型:json.loads 对比

在决定使用 ast.literal_eval() 之前,开发者还经常需要将其与标准库的 json.loads() 进行权衡。虽然两者的目标都是将文本安全反序列化为数据结构,但它们所遵循的语法规范不同:

对比维度 ast.literal_eval() json.loads() eval()(禁止使用)
引号规范 完全支持单引号 ['a', 'b'] 与双引号 ["a", "b"] 严格要求双引号 ["a", "b"],遇单引号报错 JSONDecodeError 支持单双引号
布尔值与空值 支持 Python 原生字面量:True, False, None 支持 JSON 标准关键字:true, false, null 支持当前环境中的任意值
数据类型支持 支持 list, tuple, set, dict, bytes, 数字, 字符串 仅支持 JSON 规范中的 array, object, number, string, boolean, null 支持 Python 任意对象及函数执行
解析性能 纯 Python 语法树遍历,较大量数据解析速度适中 底通常由 C 语言加速扩展实现,解析速度极快 完整编译为字节码,开销较大且高危
安全性 安全(仅限合法字面量) 安全(纯数据格式交换) 极度危险(无安全防护)

通过上述对比可以得出清晰的选型规则:

  • 如果数据来源于标准 API、微服务交换或标准存储文件,且采用标准 JSON 格式(使用双引号和 null/true/false),优先使用 json.loads(),其解析速度和跨语言兼容性更好。
  • 如果数据来源于 Python 本地环境的 str(my_list) 或 repr(my_list) 导出,列表中包含单引号字符串、元组或 True/False/None,应当使用 ast.literal_eval()。
  • 任何情况下都不应该把 eval() 列入备选方案。
Python中eval与ast.literal_eval解析字符串转列表的区别
图 4:字符串反序列化为列表的技术决策树

五、总结

在 Python 中将字符串解析为列表是一个常见且明确的开发需求。总结两者的关键区别:

  • 安全性:eval() 是全局执行器,会无差别运行字符串内的任何代码,引发远程命令执行;ast.literal_eval() 仅遍历白名单 AST 节点,遇非字面量立即拦截,安全性有保证。
  • 功能定位:eval() 用于动态代码求值;ast.literal_eval() 专为安全反序列化 Python 原生字面量设计。
  • 最佳实践:在业务代码中解析 Python repr 风格的列表文本时,统一封装带有 (ValueError, SyntaxError) 捕获与 isinstance(res, list) 断言的 ast.literal_eval() 函数;而在处理符合 JSON 标准的字符串时,首选 json.loads()。
相关标签:

免责声明:本文内容仅供参考,不代表本站观点。转载或引用请注明出处。

Python 相关产品和应用

全部下载
Selenium

Selenium

4.51.0 · Windows

用于浏览器自动化和 Web 测试的 Python 工具。

scikit-learn

scikit-learn

1.9.1 · Windows

常用 Python 机器学习算法与模型评估工具库。

Black

Black

26.10.1 · Windows

强调一致性的 Python 自动代码格式化工具。

Ruff

Ruff

0.17.1 · macOS

由 Rust 编写的高速 Python 代码检查和格式化工具。

pytest

pytest

9.1.1 · Windows

简洁而强大的 Python 自动化测试框架。

FastAPI

FastAPI

0.146.0 · Windows

高性能 Python API 框架,支持类型提示和自动接口文档。

Flask

Flask

3.1.3 · Windows

轻量灵活的 Python Web 应用框架。

Django

Django

6.1.2 · Windows

成熟的 Python Web 框架,适合构建内容站点和业务系统。

Spyder

Spyder

6.1.7 · Windows

面向科学计算与数据分析的 Python 集成开发环境。

SciPy

SciPy

1.18.1 · Windows

面向科学、工程和技术计算的 Python 算法库。