我正在开发一个需要处理大型XML文件的项目。使用Python,我不可避免地遇到了Liza Daly的文章http://www.ibm.com/developerworks/library/x-hiperfparse/。但是,我不明白她的fast_iter函数中一行的细节:
def fast_iter(context, func):
for event, elem in context:
func(elem)
elem.clear()
while elem.getprevious() is not None:
del elem.getparent()[0]
del context
为什么使用while循环而不是if语句?怎么会有多个以前的元素?
编辑:我忘了提到我在一个网站上只看到了一个我无法阅读的if版本,无论如何,http://jsome.net/blog/2010/08/18/handle-large-xml-with-python。似乎几乎所有人都按原样使用fast_iter。
如果我可以再问一个相关问题......
对于我的项目,我看到只需添加elem.clear()就可以将内存占用从17 GB减少到60 MB。进一步添加
while elem.getprevious() is not None:
del elem.getparent()[0]
仅将其减少到40 MB。我没有在变量中保存我的上下文,所以我还没有尝试添加“del context”。无论哪种方式,60 MB和40 MB都可以忽略不计。我还有理由继续添加有问题的代码吗?
while elem.getprevious() is not None:
del elem.getparent()[0]
del context
提前感谢您的帮助!
答案 0 :(得分:2)
我先回答你的第二个问题,因为它似乎更重要:
想象一下,你有一个拥有10000个直接子树的节点。只需执行elem.clear
就不会清理这些子树顶部的10000个节点,直到父节点完成为止。
如果你只有少数几个子树,这并没有太大的区别。 (即使子树非常深,每个子树都会clear
,因此只有每个子树的根存在。所以del elem.getparent()[0]
不会是这很重要。它甚至可能会伤害性能而不是帮助内存使用。所以,如果你知道你将要处理什么样的XML,你可能想要两种方式来测试它。
但是如果你不知道你的代码将要处理什么样的形状,那么可能值得删除以前的兄弟姐妹。
至于第一个问题:
我之前从未见过这篇博文,但我过去曾在两个项目中编写类似的代码,而且我从未使用while
。事实上,这对我来说似乎有些笨拙 - 你不需要在start
和end
事件中这样做;只需删除start
上的上一个孩子,或者更简单地说,在end
删除自己,对吗?
但博客文章可能会尽可能地尽量普及。首先,context
可以是iterparse
,仅提供start
或仅提供end
,对吧?同时,func
可能会使树在运行中发生变异。我有两个项目可以实时变异树而iterparse
- 它们 - 虽然它们都不需要这个循环,但这只是因为其中一个碰巧插入之后而不是之前。
正如user1093967所指出的,lxml文档部分Modifying the tree解释了您可能希望使用while
而不是if
的情况:如果您展开代码以过滤搜索你可以有多个兄弟姐妹,你已经跳过了。
无论如何,与之前的情况不同,使用while
代替if
或检查start
和end
时,这里没有实际成本,所以你不妨坚持完全通用的解决方案。
答案 1 :(得分:0)
您可以通过在循环中添加print elem.getprevious() is not None
并查看每个节点是否获得多个True
来轻松找到自己。
但是,即使在您的应用程序中没有必要while
循环,func()
也可能添加节点,因此......比抱歉更安全。出于同样的原因,我假设,她每次都通过循环调用getparent()
而不是在进入循环之前存储父级,而只是在循环体中引用它,这会更快...... {{1理论上可以改变父级。
如果额外的内存不是问题,我不会打扰。执行更多Python代码比执行更少的代码要慢。
顺便说一下,假设func()
类具有与Python列表类似的存储特性,每次通过循环时删除第一个子元素可能从性能上可能是次优的立场。您是否注意到将取决于您的文档,但是以相反的顺序迭代它们并删除下一个节点而不是之前的节点可能会稍微快一些,假设顺序无关紧要(或者之后可以轻松纠正)。 / p>