在 python 的 ElemenTree 库中如何仅对外层使用 iterparse
In python's ElemenTree library how to use iterparse only for the outer level
我有一个包含大量记录的 XML 文件。结构就像
<root>
<record>
<somedata>here</somedata>
<complexdata>
<info>text</info>
<location>here</location>
</complexdata>
.
.
.
</record>
<record>
<somedata>not here</somedata>
<complexdata>
<info>more text</info>
<location>there</location>
</complexdata>
.
.
.
</record>
.
.
.
</root>
所以该文件包含数百万个<record>
结构,但是每个记录结构只有很少的数据并且只有一两级递归。
我想要的是用 python 的 ElementTree 库解析文件。为了减少内存占用,我使用 iterparse
遍历所有记录结构。另一方面,如果每条记录本身都被完全加载到内存中并通过普通的树方法访问,那将会很方便。
如何告诉 elementtree
用于外层 iterparse
,然后切换到将整个记录加载到对象中进行处理?
To reduce memory footprint I use iterparse to go through all record
structures. On the other hand it would be handy if each record itself
would be completely loaded into memory and accessed via normal tree
methods.
iterparse()
returns 一个 Element
,一个元素提供了普通的树方法,例如您可以使用 findall()
搜索一个元素,或者您可以遍历一个元素,例如for child in elmt:
。您的 <record>
标签作为 Element 的标签返回——您只需要在正确的时刻抓住它们:
有了这个 xml 文件:
<?xml version="1.0" encoding="UTF-8" ?>
<root>
<record level="0">
<somedata>here</somedata>
<complexdata>
<info>hello</info>
<location>here</location>
</complexdata>
<record level="1"><record level="2">records</record></record>
</record>
<record level="0">
<somedata>not here</somedata>
<record level="1"></record>
<complexdata>
<record level="1"><record level="2"></record></record>
<info>goodbye</info>
<location>there</location>
</complexdata>
</record>
</root>
此代码:
import xml.etree.ElementTree as etree
record_iter = etree.iterparse("xml.xml", ["start", "end"])
_, root = next(record_iter)
record_nesting_level = -1
for (event, elmt) in record_iter:
if elmt.tag == "record":
if event == "start":
record_nesting_level += 1
else: #then it's an "end" event
if record_nesting_level == 0: #then the elmt should be a top level record tag
print("{} -- level {}".format(elmt.tag, elmt.attrib["level"]))
#Call normal etree methods on elmt:
for child in elmt:
print("\t{}".format(child.tag))
root.clear() #empty out the root element so that at most only
#one toplevel record tag will be in memory at a time
record_nesting_level -= 1
产生这个结果:
record -- level 0
somedata
complexdata
record
record -- level 0
somedata
record
complexdata
回复评论:
next()
returns 迭代器中的下一个项目。 for-in
循环如:
for elmt in record_iter:
重复调用next(record_iter)
并将结果赋值给elmt变量。您可以随时在迭代器上手动调用 next()
。
在示例代码中,next(record_iter)
returns 迭代器中的第一项,即元组:
("start", <Element 'root'>)
以下:
_, root = next(record_iter)
只是以下的变体:
x, y = (1, 2)
print(x) #=> 1
print(y) #=> 2
我本可以写成:
x, root = next(record_iter)
但是因为我不关心事件名称,而且我永远不会使用 x
变量,所以我选择了一个名为 _
的变量。这是一个完全合法的变量名。 (学习过函数式语言的人通常会用 _
表示他们不关心的变量。)
elmt.clear()
不会删除 <record>
元素,而是删除其内容。如果让 iterparse()
将 1 亿个空 Element 对象附加到 <root>
元素,它们将耗尽内存。多少?您将不得不进行一些测试——但是既然编写 root.clear()?
一样容易,为什么还要费心呢?
我有一个包含大量记录的 XML 文件。结构就像
<root>
<record>
<somedata>here</somedata>
<complexdata>
<info>text</info>
<location>here</location>
</complexdata>
.
.
.
</record>
<record>
<somedata>not here</somedata>
<complexdata>
<info>more text</info>
<location>there</location>
</complexdata>
.
.
.
</record>
.
.
.
</root>
所以该文件包含数百万个<record>
结构,但是每个记录结构只有很少的数据并且只有一两级递归。
我想要的是用 python 的 ElementTree 库解析文件。为了减少内存占用,我使用 iterparse
遍历所有记录结构。另一方面,如果每条记录本身都被完全加载到内存中并通过普通的树方法访问,那将会很方便。
如何告诉 elementtree
用于外层 iterparse
,然后切换到将整个记录加载到对象中进行处理?
To reduce memory footprint I use iterparse to go through all record structures. On the other hand it would be handy if each record itself would be completely loaded into memory and accessed via normal tree methods.
iterparse()
returns 一个 Element
,一个元素提供了普通的树方法,例如您可以使用 findall()
搜索一个元素,或者您可以遍历一个元素,例如for child in elmt:
。您的 <record>
标签作为 Element 的标签返回——您只需要在正确的时刻抓住它们:
有了这个 xml 文件:
<?xml version="1.0" encoding="UTF-8" ?>
<root>
<record level="0">
<somedata>here</somedata>
<complexdata>
<info>hello</info>
<location>here</location>
</complexdata>
<record level="1"><record level="2">records</record></record>
</record>
<record level="0">
<somedata>not here</somedata>
<record level="1"></record>
<complexdata>
<record level="1"><record level="2"></record></record>
<info>goodbye</info>
<location>there</location>
</complexdata>
</record>
</root>
此代码:
import xml.etree.ElementTree as etree
record_iter = etree.iterparse("xml.xml", ["start", "end"])
_, root = next(record_iter)
record_nesting_level = -1
for (event, elmt) in record_iter:
if elmt.tag == "record":
if event == "start":
record_nesting_level += 1
else: #then it's an "end" event
if record_nesting_level == 0: #then the elmt should be a top level record tag
print("{} -- level {}".format(elmt.tag, elmt.attrib["level"]))
#Call normal etree methods on elmt:
for child in elmt:
print("\t{}".format(child.tag))
root.clear() #empty out the root element so that at most only
#one toplevel record tag will be in memory at a time
record_nesting_level -= 1
产生这个结果:
record -- level 0
somedata
complexdata
record
record -- level 0
somedata
record
complexdata
回复评论:
next()
returns 迭代器中的下一个项目。 for-in
循环如:
for elmt in record_iter:
重复调用next(record_iter)
并将结果赋值给elmt变量。您可以随时在迭代器上手动调用 next()
。
在示例代码中,next(record_iter)
returns 迭代器中的第一项,即元组:
("start", <Element 'root'>)
以下:
_, root = next(record_iter)
只是以下的变体:
x, y = (1, 2)
print(x) #=> 1
print(y) #=> 2
我本可以写成:
x, root = next(record_iter)
但是因为我不关心事件名称,而且我永远不会使用 x
变量,所以我选择了一个名为 _
的变量。这是一个完全合法的变量名。 (学习过函数式语言的人通常会用 _
表示他们不关心的变量。)
elmt.clear()
不会删除 <record>
元素,而是删除其内容。如果让 iterparse()
将 1 亿个空 Element 对象附加到 <root>
元素,它们将耗尽内存。多少?您将不得不进行一些测试——但是既然编写 root.clear()?