我正在使用 ElementTree 的 iterparse 函数解析以 XML 文件表示的语言字典。我正在使用生成器函数对其进行过滤,并且一些奇怪的执行顺序误解给了我一个重复的条目。这是一些设置代码(这实际上发生在函数内部,但其他细节无关紧要):
import xml.etree.cElementTree as ET
dictionary = iter(ET.iterparse("../dictionaries/language_name.xml",
events=("start", "end")))
#We can discard the original iterable, I think
过滤
然后我有一个接收迭代器并过滤它的函数(忽略全局变量,它只是为了调试问题):
def get_entries(iterparsed):
global yielded
root = next(iterparsed)[1] #iterpase gives (event, element)
yield root
for event, elem in iterparsed:
if event == "end" and elem.tag == "entry":
yielded += 1
print("Num yielded:", yielded)
print("Yielding", ET.tostring(elem, encoding="utf-8"))
yield elem
加工
然后我像这样使用它(再次,临时全局用于调试):
root = next(get_entries(dictionary))
for elem in get_entries(dictionary):
global received
received += 1
print("Num received:", received)
print("I got", ET.tostring(elem, encoding="utf-8"))
raw_input("Continue? ")
#I only yield the first item once, but receive it twice? :(
process_entry(elem) #Defined elsewhere, adds a <sgmtd> node to each entry
root.clear() #Clears the processed children of root node
输出
如果我遍历所有内容,yielded = 9050
而received = 9051
. 和有问题的输出:
Num received: 1
I got <entry><form>aː</form><ortho>a:</ortho><pos>dcadv</pos><sense><def><en>over here</en><es>acá</es></def></sense></entry>
Continue?
Num yielded: 1
Yielding <entry><form>aː</form><ortho>a:</ortho><sgmtd /><pos>dcadv</pos><sense><def><en>over here</en><es>acá</es></def></sense></entry>
Num received: 2
I got <entry><form>aː</form><ortho>a:</ortho><sgmtd /><pos>dcadv</pos><sense><def><en>over here</en><es>acá</es></def></sense></entry>
Continue?
Num yielded: 2
Yielding <entry><form>aːčáx</form><ortho>a:cháj</ortho><pos>n</pos><sense><def><en>axe</en><es>hacha</es></def></sense></entry>
Num received: 3
I got <entry><form>aːčáx</form><ortho>a:cháj</ortho><pos>n</pos><sense><def><en>axe</en><es>hacha</es></def></sense></entry>
Continue?
问题
现在,我已经检查过了,并且elem
在循环开始之前没有定义。不,文件开头没有两个相同的元素。在第一个“我收到”位之后,一切似乎都按我预期的方式工作 - 事情先产生然后收到(例如 a:cháj ax先产生,然后收到)。
更奇怪的是,第一个元素在产生之前被处理- 在 for 循环结束时没有被清除。第一次“接收”时,它没有 <sgmtd> 节点。当它第一次“yield”时,它已经有一个 <sgmtd> 节点,表明它已经被处理了。然后它再次被接收,并且(尽管有一行说if not elem.find("sgmtd"): elem.insert(2, segmented_form)
)第二个 <sgmtd> 节点被添加并写出到一个文件中。那么我的输出文件结束于:
<?xml version="1.0" encoding="UTF-8"?>
<lexicon>
<entry><form>aː</form><ortho>a:</ortho><sgmtd /><pos>dcadv</pos><sense><def><en>over here</en><es>acá</es></def></sense></entry>
<entry><form>aː</form><ortho>a:</ortho><sgmtd /><sgmtd /><pos>dcadv</pos><sense><def><en>over here</en><es>acá</es></def></sense></entry>
那么我在这里误解了什么?yield
在执行语句之前没有任何代码的情况下,如何从生成器函数“接收”一个项目?
事实证明,更改if not elem.find("sgmtd")
行以if elem.find("sgmtd") is None
停止处理重复项。我猜Element
对象不会True
像我预期的那样隐式转换。但是我还是想知道为什么会出现!