Python - 生成器函数在调用之间重置?

时间:2013-08-16 00:08:54

标签: python xml generator elementtree

我正在使用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循环结束时被清除。它第一次被“收到”,它没有&lt; sgmtd&gt;节点。当它第一次“屈服”时,它已经有一个&lt; sgmtd&gt;节点,表示它已被处理。然后再次收到,并且(尽管有一行说if not elem.find("sgmtd"): elem.insert(2, segmented_form))第二个&lt; sgmtd&gt;节点被添加并写入文件。那么我的输出文件结束了:

<?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。但我仍然想知道为什么会出现!

2 个答案:

答案 0 :(得分:2)

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

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

所以:我看到重复的原因是因为生成器函数与生成器对象的行为不同。经验教训!

答案 1 :(得分:1)

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

你的计数器出现在内部循环中,只有当内部循环yield出现时才会递增。但是,您的生成器的起始位置没有增量,它显示yield root