lxml对象化不调用自定义元素类的构造函数

1 投票
1 回答
790 浏览
提问于 2025-04-16 08:50

lxml.objectify 似乎没有调用我自定义元素类的构造函数:

from lxml import objectify, etree

class CustomLookup(etree.CustomElementClassLookup):
    def lookup(self, node_type, document, namespace, name):
        lookupmap = { 'custom' : CustomElement }
        try:
            return lookupmap[name]
        except KeyError:
            return None

class CustomElement(etree.ElementBase):
    def __init__(self):
        print("Made CustomElement")

parser = objectify.makeparser()
parser.set_element_class_lookup(CustomLookup())
root = objectify.parse(fname,parser).getroot()

假设正在解析的文件是

<custom />

我希望这能打印出“创建了 CustomElement”,但实际上并没有。有没有办法让它调用构造函数呢?

怎么会有 CustomElement 类的实例被创建,而构造函数却没有被调用呢?

>>> isinstance(root,CustomElement)
True

1 个回答

2

来自lxml文档的内容:

元素初始化

首先要知道一件事。元素类不能有__init____new__方法。也就是说,内部状态不能有,除了存储在底层XML树中的数据。元素实例是根据需要创建和回收的,所以你无法预测代理对象何时以及创建多少次。更糟糕的是,当调用__init__方法时,对象甚至还没有初始化好来表示XML标签,因此在子类中提供__init__方法并没有太大意义。

大多数使用场景不需要任何类的初始化,所以你可以暂时跳过这一部分。不过,如果你真的需要在实例化时设置你的元素类,有一种可能的方法可以做到。ElementBase类有一个_init()方法,可以被重写。这个方法可以用来修改XML树,比如构建特殊的子元素或验证和更新属性。

_init()的含义如下:

  • 它在元素类实例化时被调用一次。也就是说,当lxml创建元素的Python表示时,这个时候元素对象已经完全初始化好,可以表示树中的特定XML元素。

  • 这个方法可以完全访问XML树。可以像程序中的其他地方一样进行修改。

  • 在底层C树中,元素的Python表示可能会在XML元素的生命周期内被多次创建。子类提供的_init()代码必须特别小心,确保多次执行要么是无害的,要么通过XML树中的某种标志被阻止。后者可以通过修改属性值或添加或删除特定的子节点来实现,然后在运行初始化过程之前进行验证。

  • _init()中引发的任何异常都会通过导致元素创建的API调用传播出去。所以在这里编写代码时要小心,因为它的异常可能会在各种意想不到的地方出现。

撰写回答