有没有更好的方法在xml.etree.ElementTree中为其父元素和xpath提供元素知识

时间:2013-11-21 22:30:53

标签: python elementtree monkeypatching celementtree

我有以下代码可以使用:

import xml.etree.ElementTree as etree


def get_path(self):
    parent =  ''
    path = self.tag
    sibs = self.parent.findall(self.tag)
    if len(sibs) > 1:
        path = path + '[%s]'%(sibs.index(self)+1)
    current_node = self
    while True:
        parent = current_node.parent
        if not parent:
            break
        ptag = parent.tag
        path = ptag + '/' + path
        current_node = parent
    return path

etree._Element.get_path = get_path
etree._Element.parent = None

class XmlDoc(object):
    def __init__(self):
        self.root = etree.Element('root')
        self.doc = etree.ElementTree(self.root)

    def SubElement(self, parent, tag):
        new_node = etree.SubElement(parent, tag)
        new_node.parent = parent
        return new_node

doc = XmlDoc()
a1 = doc.SubElement(doc.root, 'a')
a2 = doc.SubElement(doc.root, 'a')
b = doc.SubElement(a2, 'b')
print etree.tostring(doc.root), '\n'
print 'element:'.ljust(15), a1
print 'path:'.ljust(15), a1.get_path()
print 'parent:'.ljust(15), a1.parent, '\n'
print 'element:'.ljust(15), a2
print 'path:'.ljust(15), a2.get_path()
print 'parent:'.ljust(15), a2.parent, '\n'
print 'element:'.ljust(15), b
print 'path:'.ljust(15), b.get_path()
print 'parent:'.ljust(15), b.parent

这导致此输出:

<root><a /><a><b /></a></root> 

element:        <Element a at 87e3d6c>
path:           root/a[1]
parent:         <Element root at 87e3cec> 

element:        <Element a at 87e3fac>
path:           root/a[2]
parent:         <Element root at 87e3cec> 

element:        <Element b at 87e758c>
path:           root/a/b
parent:         <Element a at 87e3fac>

现在这与原始代码有很大的不同,但我不允许分享。

这些函数效率不是太低,但是当我从cElementTree切换到ElementTree时会有一个显着的性能下降,但是根据我的实验,似乎猴子修补cElementTree是不可能的,所以我不得不切换。

我需要知道的是,是否有一种方法可以将方法添加到cElementTree,或者是否有更有效的方法来实现这一点,这样我就可以恢复一些性能。

只是为了让你知道我正在考虑实现选择的静态类型并使用cython进行编译,但出于某些原因我真的不想这样做。

谢谢你看看。

编辑:对于错误使用术语后期绑定感到抱歉。有时我的词汇会留下一些需要的东西。我的意思是“猴子补丁。”

编辑:@Corley Brigman,盖伊:非常感谢您解决问题,但是(我应该在原帖中说明)我在使用lxml之前完成了这个项目,这是一个很棒的图书馆这使得编码变得轻而易举,但由于新的要求(这需要作为一个名为Splunk的产品的插件实现),它将我与Splunk附带的python 2.7解释器联系起来,并消除了添加第三方库的可能性,但django除外

2 个答案:

答案 0 :(得分:1)

如果您需要父母,请使用lxml - 它在内部跟踪父母,并且在幕后仍然是C,所以它非常快。

然而......请注意跟踪父母存在权衡,因为给定节点只能有一个父节点。这通常不是问题,但是,如果您执行以下操作,您将在cElementTree与lxml中获得不同的结果:

p = Element('x')
q = Element('y')
r = SubElement(p, 'z')
q.append(r)

cElementTree:

dump(p)
<x><z /></x>
dump(q)
<y><z /></y>

LXML:

dump(p)
<x/>
dump(q)
<y>
  <z/>
</y>

由于父母被跟踪,显然一个节点只能有一个父节点。如您所见,元素r复制到cElementTree中的两个树,并且在lxml中重新设置/移动

可能只有少数用例需要考虑,但需要注意的事项。

答案 1 :(得分:0)

你可以使用xpath,例如:

import lxml.html

def get_path():
    for e in doc.xpath("//b//*"):
        print e

应该工作,虽然没有测试......