Python 中字符串、列表与对象解析实战:从字符串到 List,再到 Document 内容提取
在实际开发中,我们经常会遇到这样的数据:
- 看起来像
list,实际上是str - 看起来像对象,实际上只是对象的字符串描述
- 从日志、数据库、接口返回的数据中恢复 Python 对象
今天通过两个真实案例,讲解如何正确处理。
一、问题一:字符串形式的 List 如何转换成真正 List?
1.1 场景
例如:
raw_str = '["qwen-turbo","qwen-plus","deepseek"]'很多初学者会认为:
print(type(raw_str))应该是:
<class 'list'>但是实际:
print(type(raw_str))结果:
<class 'str'>原因:
它只是一个字符串。
里面的:
["qwen-turbo","qwen-plus","deepseek"]只是字符。
1.2 错误方式:eval()
很多人第一反应:
result = eval(raw_str)结果:
['qwen-turbo', 'qwen-plus', 'deepseek']看起来可以。
但是:
不推荐!
原因:
eval()会执行 Python 代码。
例如:
data = "__import__('os').system('rm -rf /')" eval(data)如果数据来自:
- 用户输入
- 网络请求
- 数据库
可能造成安全问题。
1.3 推荐方式:ast.literal_eval()
Python 提供安全解析:
import ast raw_str = '["qwen-turbo","qwen-plus","deepseek"]' models = ast.literal_eval(raw_str) print(models) print(type(models))输出:
['qwen-turbo', 'qwen-plus', 'deepseek'] <class 'list'>1.4 literal_eval 支持哪些类型?
例如:
list
'["a","b"]'转换:
["a","b"]dict
"{'name':'deepseek'}"转换:
{ "name":"deepseek" }tuple
"(1,2,3)"转换:
(1,2,3)二、问题二:字符串中的 Document 对象如何获取 page?
这是 RAG 项目中非常常见的问题。
例如:
"[Document(metadata={'pk':12,'page':2}, page_content='xxx')]"很多人看到:
Document以为:
documents[0].page_content可以访问。
但是实际上:
type(data)结果:
<class 'str'>它不是 Document。
只是:
一个包含 Document 文本描述的字符串。
三、为什么不能直接取 page?
错误:
data = "[Document(metadata={'page':2},page_content='hello')]" print(data[0].page)错误:
AttributeError因为:
data:
实际结构:
String | | 字符 | | [ D o c u m e n t ( ... )Python 不知道:
Document是什么对象。
四、正确解决方案一:不要让对象变字符串(最佳方案)
如果数据来源是 LangChain:
例如:
from langchain_core.documents import Document docs = [ Document( metadata={ "pk":12, "page":2 }, page_content="测试内容" ) ]此时:
print(type(docs))输出:
list里面元素:
print(type(docs[0]))输出:
Document那么直接:
for doc in docs: print(doc.metadata["page"]) print(doc.page_content)输出:
2 测试内容五、真实场景:数据库里面保存成字符串怎么办?
很多项目:
比如:
mysql 字段: documents 内容: "[Document(...)]"读取以后:
content = row.documents变成:
str怎么办?
方法1:正则提取 page_content
如果只是想获取文本:
import re text = """ Document(metadata={'page':2}, page_content='hello world') """ contents = re.findall( r"page_content='(.*?)'", text, re.S ) print(contents)结果:
[ 'hello world' ]然后:
result = "\n".join(contents)得到:
hello world六、方法2:重新构造 Document 对象
推荐在 RAG 项目中使用。
假设:
字符串:
doc_str里面:
metadata page_content解析:
from langchain_core.documents import Document docs=[] for item in解析后的数据: docs.append( Document( metadata=item["metadata"], page_content=item["page_content"] ) )之后:
for doc in docs: print(doc.metadata["page"]) print(doc.page_content)七、如果字符串格式固定,可以使用 ast + 转换
例如:
text = """ [ { "metadata":{ "page":2 }, "page_content":"hello" } ] """转换:
import ast data = ast.literal_eval(text) for item in data: print(item["metadata"]["page"]) print(item["page_content"])输出:
2 hello八、RAG项目中的实际应用
在知识库系统里面:
流程:
PDF | ↓ Document对象 Document( metadata={ page:1 }, page_content="政策内容" ) | ↓ Embedding | ↓ Vector DB检索回来:
应该保持:
List[Document]例如:
docs = vector_store.similarity_search(question)返回:
[ Document(...), Document(...) ]然后:
for doc in docs: page = doc.metadata["page"] content = doc.page_content生成引用:
来源: 政策文件.pdf 第2页 内容: xxxx九、两个问题总结
| 问题 | 本质 | 解决方案 |
|---|---|---|
| 字符串 List 转 List | str → list | ast.literal_eval() |
| 字符串 Document 转对象 | str → Document | 不要序列化,或者重新构造 |
| 获取 page | 读取 metadata | doc.metadata["page"] |
| 获取文本 | 读取 page_content | doc.page_content |
十、最终记忆
开发中遇到:
看起来像对象先问:
print(type(data))如果:
str不要直接:
data.xxx先恢复对象。
RAG 开发尤其注意:
Document对象 ↓ 不要变成字符串 ↓ 保持 List[Document] ↓ metadata 保存来源 ↓ page_content 保存正文这也是为什么 LangChain、LlamaIndex 等框架一直强调:
检索阶段保持 Document 对象结构,不要提前转成字符串。