lxml对象化不调用自定义元素类的构造函数
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 个回答
来自lxml
文档的内容:
元素初始化
首先要知道一件事。元素类不能有
__init__
或__new__
方法。也就是说,内部状态不能有,除了存储在底层XML树中的数据。元素实例是根据需要创建和回收的,所以你无法预测代理对象何时以及创建多少次。更糟糕的是,当调用__init__
方法时,对象甚至还没有初始化好来表示XML标签,因此在子类中提供__init__
方法并没有太大意义。大多数使用场景不需要任何类的初始化,所以你可以暂时跳过这一部分。不过,如果你真的需要在实例化时设置你的元素类,有一种可能的方法可以做到。ElementBase类有一个
_init()
方法,可以被重写。这个方法可以用来修改XML树,比如构建特殊的子元素或验证和更新属性。
_init()
的含义如下:
它在元素类实例化时被调用一次。也就是说,当lxml创建元素的Python表示时,这个时候元素对象已经完全初始化好,可以表示树中的特定XML元素。
这个方法可以完全访问XML树。可以像程序中的其他地方一样进行修改。
在底层C树中,元素的Python表示可能会在XML元素的生命周期内被多次创建。子类提供的
_init()
代码必须特别小心,确保多次执行要么是无害的,要么通过XML树中的某种标志被阻止。后者可以通过修改属性值或添加或删除特定的子节点来实现,然后在运行初始化过程之前进行验证。在
_init()
中引发的任何异常都会通过导致元素创建的API调用传播出去。所以在这里编写代码时要小心,因为它的异常可能会在各种意想不到的地方出现。