我正在使用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
。但我仍然想知道为什么会出现!
答案 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
。