
批量生成合同、报告或说明书时,文档里总有几十处需要同步的信息:版本号、生成日期、客户名称、审批人。用字符串替换逐个改写正文,不仅容易漏改,改动也没有统一入口------模板一旦被人工编辑过,替换逻辑就很容易失配。
Word 自带的"文档变量"(Document Variables)正是为这个场景设计的。它是一组保存在文档内部的键值对,正文通过 DOCVARIABLE 域引用它们:变量里存 "2.4",正文里就显示 "2.4";把变量改成 "3.1" 并刷新域,文档中所有引用位置同步变成 "3.1"。这相当于把文档内容变成了可被脚本填充的模板。
本文用 Spire.Doc for Python 演示文档变量的完整生命周期:添加与引用、读取、更新、删除,以及最容易被忽略的域刷新时机。
环境准备
安装 Spire.Doc 并引入模块:
bash
pip install Spire.Doc
python
from spire.doc import *
from spire.doc.common import *
变量集合挂在文档对象上,通过 document.Variables 访问,类型是 VariableCollection。
添加变量并在正文中引用
添加变量分两步:先用 Variables.Add(name, value) 存入键值对,再用 AppendField 在正文中插入指向该变量的 DOCVARIABLE 域。
python
from spire.doc import *
from spire.doc.common import *
document = Document()
section = document.AddSection()
# A variable that is referenced from the body text.
paragraph = section.AddParagraph()
paragraph.AppendText("Contract version: ")
paragraph.AppendField("Version", FieldType.FieldDocVariable)
paragraph.AppendText(", generated on ")
paragraph.AppendField("BuildDate", FieldType.FieldDocVariable)
document.Variables.Add("Version", "2.4")
document.Variables.Add("BuildDate", "2026-11-13")
document.Variables.Add("Reviewer", "QA team")
document.IsUpdateFields = True
document.SaveToFile("DocumentVariables.docx", FileFormat.Docx2013)
document.Close()
这里有三个关键点:
AppendField("Version", FieldType.FieldDocVariable)的第一个参数是变量名,域代码会写成DOCVARIABLE Version;同名变量可以在正文中被引用任意多次。- 变量名区分大小写。
Version和version是两个独立条目,写错大小写会导致引用取不到值。 document.IsUpdateFields = True让 Spire 在保存时统一刷新域结果。不设置这一句,域里的旧值会被原样保留------即使变量已经改成了新值。
最后一点是排查这类问题时最常踩的坑:不刷新域,正文里显示的仍然是变量上一次的值,哪怕变量本身已经改过了。
读取文档中的变量
VariableCollection 提供按索引和按名称两种读取方式:
python
from spire.doc import *
document = Document()
document.LoadFromFile("DocumentVariables.docx")
print("count:", document.Variables.Count)
for i in range(document.Variables.Count):
name = document.Variables.GetNameByIndex(i)
value = document.Variables.GetValueByIndex(i)
print(f"[{i}] {name} = {value}")
print("by name:", document.Variables["Version"])
document.Close()
Count是变量总数,也是遍历时的边界。GetNameByIndex(i)与GetValueByIndex(i)按下标取名称和值,适合遍历。document.Variables["Version"]按名称直接取值,更适合已知名字的场景。
需要注意的是,按名称读取一个不存在的变量不会抛异常,而是返回 None。在自动化流程里判断变量是否存在时,应当显式检查这个返回值,而不是依赖异常。
更新变量的值
修改变量值的写法比想象中需要注意:Add() 不允许添加同名变量,重复添加会抛出 Argument_AddingDuplicateWithKey 异常;Python 绑定也不支持 document.Variables["Version"] = "3.1" 这种下标赋值(会抛出 TypeError)。可靠的做法是先移除旧条目再写入新值:
python
from spire.doc import *
document = Document()
document.LoadFromFile("DocumentVariables.docx")
# Add() rejects a duplicate name, so remove the old entry before adding the new value.
document.Variables.Remove("Version")
document.Variables.Add("Version", "3.1")
print("updated value:", document.Variables["Version"])
document.IsUpdateFields = True
document.SaveToFile("DocumentVariablesUpdated.docx", FileFormat.Docx2013)
document.Close()
改完后再次读取 document.Variables["Version"],返回的就是 "3.1";保存后的文档里,DOCVARIABLE Version 域的显示结果也已经变成 "3.1"。
如果忘了设置 IsUpdateFields = True,保存出来的文档中域结果仍是旧值 "2.4"------变量本身已经更新,只是正文里的显示文本没有跟着刷新。
删除变量
移除变量用 Remove(name):
python
from spire.doc import *
document = Document()
document.LoadFromFile("DocumentVariablesUpdated.docx")
document.Variables.Remove("BuildDate")
print("count after remove:", document.Variables.Count)
document.IsUpdateFields = True
document.SaveToFile("DocumentVariablesRemoved.docx", FileFormat.Docx2013)
document.Close()
删除后 Count 从 3 变为 2。正文里的 DOCVARIABLE 域仍然保留,但刷新后不再显示任何内容------变量消失,引用它的位置变成空白。
对不存在的变量名调用 Remove() 不会报错,会安静地返回 None,所以批量清理时不必逐个判断。
变量保存在哪里
文档变量并不是普通的正文内容,它被写进 docx 包里的 word/settings.xml,结构与 Word 自身保存的一致:
xml
<w:docVars>
<w:docVar w:name="Version" w:val="2.4" />
<w:docVar w:name="BuildDate" w:val="2026-11-13" />
<w:docVar w:name="Reviewer" w:val="QA team" />
</w:docVars>
这个特性有两个直接好处:一是变量和正文分离,改动变量不会破坏文档排版;二是任何能解析 docx 的工具都能读出这些键值对,便于外部系统校验文档内容是否与预期一致。
实用技巧
- 模板加变量:把一份文档做成模板,用变量承载版本号、日期这类易变信息,脚本只负责写入变量并刷新域,比逐处替换正文稳定得多。
- 先判断再读取 :按名称取值返回
None而非抛异常,批量处理前先做一次存在性检查。 - 更新前先移除 :
Add()对重复名称会抛Argument_AddingDuplicateWithKey,"先 Remove 再 Add" 是唯一可靠的覆盖方式。 - 保存前刷新域 :
document.IsUpdateFields = True是让正文显示与变量值保持一致的关键开关。 - 变量名区分大小写 :命名统一使用驼峰或全大写,避免
Version与version混用。 - 及时释放资源 :文档用完调用
document.Close()。
结论
文档变量提供了把 Word 文档变成"可填充模板"的方式:用 Variables.Add() 写入键值对,用 AppendField 配合 FieldType.FieldDocVariable 在正文中建立引用,用 Count、GetNameByIndex、GetValueByIndex 和名称索引读取现有变量,用"先 Remove 再 Add"更新取值,用 Remove() 清理不再需要的条目。
真正需要留意的只有两点:Add() 不接受重复名称,赋值必须先移除旧条目;IsUpdateFields = True 决定正文显示是否随变量更新。把这两点处理好,同一份模板就能稳定支撑批量文档生成。