QUESTION

文档已经是字典,为什么还要写row = dict(doc)?

A

dict(doc)创建新的外层字典,避免附加向量时直接修改DOCS中的原始文档。

DETAILED ANSWER

直接结论

doc确实已经是字典,但dict(doc)的目的不是把它“转换成字典”,而是创建一个新的外层字典。这样给row增加title_vectorcontent_vector时,不会直接修改DOCS里的原始文档。

不复制会发生什么

如果写成:

row = doc
row["title_vector"] = title_vector

rowdoc只是同一个字典对象的两个名字:

row ──┐
      ├──> 同一个字典
doc ──┘

修改row就等于修改doc,而doc又来自DOCS,所以原始数据也会被加入向量字段。

dict(doc)做了什么

row = dict(doc)

执行后,外层字典变成两个不同对象:

doc ──> 原始字典
row ──> 新的外层字典

它们一开始拥有相同的键和值,但增加或删除row最外层的键,不会影响doc

row["title_vector"] = title_vector
row["content_vector"] = content_vector

因此代码完成的是:

保留原始文档字段
+ 附加标题向量
+ 附加正文向量
→ 形成准备写入向量数据库的新记录

为什么叫浅拷贝

dict(doc)只复制最外层字典,里面已有的可变对象仍可能被共享。

doc = {
    "title": "示例",
    "tags": ["Python", "RAG"],
}
row = dict(doc)

此时:

row["title"] = "新标题"

不会改变doc["title"],因为是在替换row最外层的值;但是:

row["tags"].append("向量数据库")

会同时影响doc["tags"],因为两个字典仍引用同一个列表。

如果需要连嵌套列表和嵌套字典也完全独立,才考虑copy.deepcopy(doc)

放回原代码理解

rows = []
for doc, title_vector, content_vector in zip(
    DOCS,
    title_vectors,
    content_vectors,
):
    row = dict(doc)
    row["title_vector"] = title_vector
    row["content_vector"] = content_vector
    rows.append(row)

每轮都从原始文档复制出一条新记录,再附加与该文档同位置的两个向量,最后放入rows。原始DOCS仍保持只有pktitlecontentcategory等字段。