1

我正在使用 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 = 9050received = 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像我预期的那样隐式转换。但是我还是想知道为什么会出现!

4

2 回答 2

2

@Chad Miller@Jochen Ritzel 都指出我没有计算我所产生的根元素。这是故意的——我认为会发生的是我的生成器函数永远不会重置,就像生成器对象不会重置一样。所以当我开始循环时for elem in get_entries(dictionary),我认为根元素已经被消耗掉了。

但是,如果我在生成根元素之前添加一个打印语句,它会被打印两次。我看到的数据重复是由elem.insert(2, segmented_form)在根上调用引起的,其中segmented_form涉及使用elem.find(因此搜索其子级)并获取树的第一个元素。

所以:我看到重复的原因是因为生成器函数的行为与生成器对象不同。学过的知识!

于 2013-08-17T07:19:50.667 回答
1

看起来您过滤器中的计数器是错误的。

您的计数器出现在内部循环中,并且仅在该内部循环发生时才会增加yield。但是它说的生成器的启动没有增量yield root

于 2013-08-16T02:14:29.457 回答