xml.etree.ElementTree 的 findall() 和 iterfind() 有什么区别

What's difference between findall() and iterfind() of xml.etree.ElementTree

我用下面的方法写了一个程序

from xml.etree.ElementTree import ET

xmlroot = ET.fromstring([my xml content])

for element in xmlroot.iterfind(".//mytag"):
    do some thing

它在我的 python (v2.7.1) 上运行良好,但在我将它复制到另一台安装了 python v2.6.x 的计算机后,iterfind() 是不支持,python document,下面列出了描述

findall(match)

Finds all matching subelements, by tag name or path. Returns a list containing all matching elements in document order.

iterfind(match)

Finds all matching subelements, by tag name or path. Returns an iterable yielding all matching elements in document order.

New in version 2.7.

我的问题是:这2个功能是否相同?这两个函数有什么区别

如果你这样做

for element in xmlroot.iterfind(".//mytag"):
    do some thing

然后将从 XML 文件中一次检索一个元素(每个循环一个元素)。

如果你这样做

for element in xmlroot.findall(".//mytag"):
    do some thing

所有元素将被一次检索并存储到一个(临时)列表中。只有这样 for 循环才会开始遍历该列表。

这意味着第二种方法在开始时需要更长的时间(因为它必须构建该列表)并且使用更多的内存(同样的原因)。此外,如果您需要在到达最后一个元素之前退出 for 循环,那么您将完成不必要的工作。另一方面,一旦进入 for 循环,第二种方法可能会更快一些。通常,第一种方法 ("lazy evaluation") 的好处超过了这个缺点。

在你的情况下,切换到 findall 可能是安全的。

正如您在 link iter 中所说,找到 return 一个生成器(产量)并找到所有 return 一个列表。

唯一的区别就在那里,您可以查看here例如这两种类型之间的区别。

在这种情况下主要是内存性能。

如文档中所示 -

  1. findall returns 匹配 match xpath 的完整元素列表,我们可以使用下标来访问它们,示例-

    >>> root = ET.fromstring("<a><b>c</b></a>")
    >>> root.findall("./b")
    [<Element 'b' at 0x02048C90>]
    >>> lst = root.findall("./b")
    >>> lst[0]
    <Element 'b' at 0x02048C90>
    

我们也可以使用for循环来遍历列表。

  1. iterfind returns 是一个迭代器(generator),它不return list,在这种情况下我们不能使用下标来访问元素,我们只能在接受迭代器的地方使用它,例如在 for 循环中。

iterfind 在您实际想要遍历 returned 列表的情况下会比 findall 快(根据我的经验,这是大部分时间),因为 findall 必须在 returning,而 iterfind 仅在迭代时找到(产生)与 match 匹配的下一个元素并调用 next(iter)(这是在使用 [=15 遍历列表时内部调用的内容) =] 或这样的结构)。

在您需要列表的情况下,两者的时间安排似乎相似。

两种情况的性能测试-

In [1]: import xml.etree.ElementTree as ET

In [2]: x = ET.fromstring('<a><b>c</b><b>d</b><b>e</b></a>')

In [3]: def foo(root):
   ...:     d = root.findall('./b')
   ...:     for  y in d:
   ...:         pass
   ...: 

In [4]: def foo1(root):
   ...:     d = root.iterfind('./b')
   ...:     for y in d:
   ...:         pass
   ...: 

In [5]: %timeit foo(x)
100000 loops, best of 3: 9.24 µs per loop

In [6]: %timeit foo1(x)
100000 loops, best of 3: 6.65 µs per loop

In [7]: def foo2(root):
   ...:     return root.findall('./b')
   ...: 

In [8]: def foo3(root):
   ...:     return list(root.iterfind('./b'))
   ...: 

In [9]: %timeit foo2(x)
100000 loops, best of 3: 8.54 µs per loop

In [10]: %timeit foo3(x)
100000 loops, best of 3: 8.4 µs per loop