10.3. 查询缓冲节点

kgp.py 使用了多种技巧,在你进行 XML 处理时,它们或许能派上用场。第一个就是,利用输入文档的结构稳定特征来构建节点缓冲。

一个语法文件定义了一系列的 ref 元素。每个 ref 包含了一个或多个 p 元素,p 元素则可以包含很多不同的东西,包括 xref。对于每个 xref,你都能找到相对应的 ref 元素 (它们具有相同的 id 属性),然后选择 ref 元素的子元素之一进行解析。(在下一部分中你将看到是如何进行这种随机选择的。)

语法的构建方式如下:先为最小的片段定义 ref 元素,然后使用 xref 定义“包含”第一个 ref 元素的 ref 元素,等等。然后,解析“最大的”引用并跟着 xref 跳来跳去,最后输出真实的文本。输出的文本依赖于你每次填充 xref 时所做的 (随机) 决策,所以每次的输出都是不同的。

这种方式非常灵活,但是有一个不好的地方:性能。当你找到一个 xref 并需要找到相应的 ref 元素时,会遇到一个问题。xrefid 属性,而你要找拥有相同 id 属性的 ref 元素,但是没有简单的方式做到这件事。较慢的方式是每次获取所有 ref 元素的完整列表,然后手动遍历并检视每一个 id 属性。较快的方式是只做一次,然后以字典形式构建一个缓冲。

例 10.14. loadGrammar

    def loadGrammar(self, grammar):                         
        self.grammar = self._load(grammar)                  
        self.refs = {}                                       1
        for ref in self.grammar.getElementsByTagName("ref"): 2
            self.refs[ref.attributes["id"].value] = ref      3 4
1 从创建一个空字典 self.refs 开始。
2 正如你在第 9.5 节 “搜索元素”中看到的,getElementsByTagName 返回所有特定名称元素的一个列表。你可以很容易地得到所有 ref 元素的一个列表,然后遍历这个列表。
3 正如你在第 9.6 节 “访问元素属性”中看到的,使用标准的字典语法,你可以通过名称来访问个别元素。所以,self.refs 字典的键将是每个 ref 元素的 id 属性值。
4 self.refs 字典的值将是 ref 元素本身。如你在第 9.3 节 “XML 解析”中看到的,已解析 XML 文档中的每个元素、节点、注释和文本片段都是一个对象。

只要构建了这个缓冲,无论何时你遇到一个 xref 并且需要找到具有相同 id 属性的 ref 元素,都只需在 self.refs 中查找它。

例 10.15. 使用 ref 元素缓冲

    def do_xref(self, node):
        id = node.attributes["id"].value
        self.parse(self.randomChildElement(self.refs[id]))

你将在下一部分探究 randomChildElement 函数。