Python中两种“伪列表“字符串的解析技巧

发布时间:2026/8/11 4:49:10
Python中两种“伪列表“字符串的解析技巧 Python中两种伪列表字符串的解析技巧在 Python 开发中我们经常会遇到看起来像 list但实际是 str的数据。本文通过两道实战题目带你掌握两种不同场景下的解析方法。一、引言你是否遇到过这样的情况data[qwen-turbo,qwen-plus,deepseek]print(type(data))# class str 明明看起来是个列表为什么类型是字符串更头疼的是有时候字符串里还包含自定义对象比如 LangChain 的Documentdata[Document(metadata{pk: 12, page: 2}, page_content...)]这两种情况解析方法完全不同本文通过两道题带你彻底搞懂。二、第一题标准格式字符串转列表2.1 题目raw_str[qwen-turbo,qwen-plus,deepseek]# 将其转换为 list2.2 分析这是一个标准的 JSON 格式字符串内容是字符串数组。2.3 解决方案方法一json.loads()推荐importjson raw_str[qwen-turbo,qwen-plus,deepseek]resultjson.loads(raw_str)print(result)# [qwen-turbo, qwen-plus, deepseek]print(type(result))# class list优点速度快标准库支持JSON 格式通用性强方法二ast.literal_eval()importast raw_str[qwen-turbo,qwen-plus,deepseek]resultast.literal_eval(raw_str)print(result)# [qwen-turbo, qwen-plus, deepseek]print(type(result))# class list优点安全性高只解析字面量不会执行恶意代码方法三eval()不推荐raw_str[qwen-turbo,qwen-plus,deepseek]resulteval(raw_str)print(result)# [qwen-turbo, qwen-plus, deepseek]缺点有安全风险会执行任意 Python 代码生产环境慎用2.4 三种方法对比方法速度安全性适用场景json.loads()⭐⭐⭐ 最快⭐⭐ 安全JSON 格式字符串ast.literal_eval()⭐⭐ 一般⭐⭐⭐ 最安全Python 字面量格式eval()⭐⭐ 一般⭐ 危险不推荐使用三、第二题对象格式字符串提取字段3.1 题目raw_str[Document(metadata{pk: 12, page: 2}, page_content2. 费⽤报销业务活动中产⽣的费⽤...), Document(metadata{pk: 27, page: 2}, page_content2. 费⽤报销业务活动中产⽣的费⽤...), Document(metadata{pk: 14, page: 3}, page_content3. 最后警告情节严重留司察看...)]# 问题如何获取里面的 page 属性3.2 分析这个字符串包含LangChain 的 Document 对象它不是标准 JSON而是 Python 的repr()输出格式。为什么 json.loads() 不行importjson json.loads(raw_str)# ❌ 报错json.decoder.JSONDecodeError原因字符串中使用了单引号JSON 要求双引号包含Document(...)这种自定义对象不是 JSON 支持的数据类型为什么 ast.literal_eval() 也不行importast ast.literal_eval(raw_str)# ❌ 报错ValueError: malformed node原因ast.literal_eval()只能解析 Python 字面量字符串、数字、列表、字典等不能解析函数调用Document(...)。3.3 解决方案正则表达式importrefromcollectionsimportdefaultdict raw_str[Document(metadata{pk: 12, page: 2}, page_content...), ...]# 方法1只提取 pagepagesre.findall(rpage:\s*(\d),raw_str)pages[int(p)forpinpages]print(pages)# [2, 2, 3]# 方法2提取 page page_contentdocsre.findall(rDocument\(metadata\{.*?page:\s*(\d).*?page_content(.*?)\),raw_str,re.DOTALL)# 按页码分组page_contentsdefaultdict(list)forpage,contentindocs:page_contents[int(page)].append(content)# 按页码排序输出forpageinsorted(page_contents.keys()):combined\n.join(page_contents[page])print(f 第{page}页 )print(combined)print()3.4 正则表达式解析rpage:\s*(\d)部分含义page:匹配字面量page:\s*匹配 0 或多个空白字符(\d)捕获 1 或多个数字这是我们想要的rDocument\(metadata\{.*?page:\s*(\d).*?page_content(.*?)\)部分含义Document\(匹配Document(metadata\{匹配metadata{.*?非贪婪匹配任意字符page:\s*(\d)捕获 page 的值page_content(.*?)捕获 page_content 的内容\)匹配)四、完整代码4.1 第一题完整代码importjsonimportastdefparse_json_list(raw_str:str)-list:解析 JSON 格式的字符串列表returnjson.loads(raw_str)defparse_python_list(raw_str:str)-list:解析 Python 字面量格式的字符串列表returnast.literal_eval(raw_str)# 测试raw_str[qwen-turbo,qwen-plus,deepseek]result1parse_json_list(raw_str)result2parse_python_list(raw_str)print(fjson.loads:{result1})print(fast.literal_eval:{result2})4.2 第二题完整代码importrefromcollectionsimportdefaultdictdefextract_pages(raw_str:str)-list:从 Document 字符串中提取所有 pagereturn[int(p)forpinre.findall(rpage:\s*(\d),raw_str)]defextract_docs_by_page(raw_str:str)-dict:从 Document 字符串中提取 page 和 page_content按页分组docsre.findall(rDocument\(metadata\{.*?page:\s*(\d).*?page_content(.*?)\),raw_str,re.DOTALL)page_contentsdefaultdict(list)forpage,contentindocs:page_contents[int(page)].append(content)returndict(page_contents)defcombine_by_page(raw_str:str)-str:按页码拼接所有内容page_contentsextract_docs_by_page(raw_str)result[]forpageinsorted(page_contents.keys()):combined\n.join(page_contents[page])result.append(f 第{page}页 \n{combined})return\n\n.join(result)# 测试raw_str[Document(metadata{pk: 12, page: 2}, page_content2. 费⽤报销...), Document(metadata{pk: 27, page: 2}, page_content2. 费⽤报销...), Document(metadata{pk: 14, page: 3}, page_content3. 最后警告...)]print(提取的页码,extract_pages(raw_str))print(\n按页拼接结果)print(combine_by_page(raw_str))五、对比总结对比项第一题纯数据第二题对象格式字符串格式JSON 标准格式Python repr 格式数据类型基本类型字符串、数字自定义对象Document引号类型双引号单引号能否用 json.loads()✅ 能❌ 不能能否用 ast.literal_eval()✅ 能❌ 不能能否用 eval()✅ 能⚠️ 需要 import Document推荐方案json.loads()re.findall()选型建议遇到字符串形式的列表 │ ├─ 是标准 JSON 格式 → json.loads() │ ├─ 是 Python 基本字面量 → ast.literal_eval() │ └─ 包含自定义对象 → 正则表达式提取六、常见问题Q1为什么不推荐 eval()eval()会执行任意 Python 代码存在安全风险# 恶意输入malicious__import__(os).system(rm -rf /)eval(malicious)# 危险Q2正则表达式太难了有更简单的方法吗如果字符串格式固定也可以用字符串分割# 简单粗暴的方法pages[]forpartinraw_str.split(page: ):ifpartandpart[0].isdigit():pages.append(int(part.split(,)[0]))但正则更通用、更可靠。Q3LangChain 的 Document 对象为什么不是标准 JSON因为Document是 Python 类它的repr()输出是 Python 语法不是 JSON。这是 Python 对象序列化的常见问题。七、总结标准格式用标准工具JSON 字符串用json.loads()复杂格式用正则包含自定义对象的字符串正则是最佳选择安全第一避免使用eval()除非你能完全信任输入源掌握这两种技巧以后再遇到看起来像 list 的字符串就不会懵了