这里的根本问题是它xml.etree.ElementTree.Element
不是为子类而设计的。
我不认为这是故意的,他们只是没想到有人会继承它,也没有考虑过。在 Python 3 中,您用纯 Python 编写的几乎所有内容都可以很好地进行子类化,但 C-API 类是另一回事。如果您查看xml.etree.ElementTree.Element
,它实际上是_elementtree.Element
,它是用 C 实现的(作为cElementTree
2.x 的一个简单端口)。
让我们通过一个精简的实现来看看这个问题:
import xml.etree.ElementTree as ET
class KML(ET.Element):
def __init__(self, *args):
super().__init__('kml')
k = KML()
k.doc = 'Doc'
AttributeError
一旦您尝试分配给 ,这将引发k.doc
。为什么?好吧,__setattr__
这调用了. 与您尝试使用 an而不是子类或使用.ET.Element
ET.Element
AttributeError
ET.Element
int
但:
class KML(ET.Element):
def __init__(self, *args):
super().__init__('kml')
self.doc = 'Doc'
k = KML()
现在也不例外……但它也没有设置属性,正如您self.doc
在设置后立即尝试访问或k.doc
在创建后立即访问所看到的那样。这是因为属性创建异常在__new__
or中时会被吞没__init__
,这使得问题更难调试。
那么,你会怎么做呢?
一种可能性是__setattr__
自己实现。
对于所有非子类化友好的 C-API 类来说,情况并非如此,但在这种情况下,您实际上有一个适当__dict__
的 ( object
) 实现__setattr__
和朋友使用的,您只是没有该实现。
您可以对其进行修补,或尝试设置正确的多重继承(但Element
由于与原始问题类似的原因,这会出现问题)。
但我认为明确地编写它要简单得多:
def __setattr__(self, attr, value):
self.__dict__[attr] = value
def __delattr__(self, attr):
del self.__dict__[attr]
另一种可能性是通过阻止 C 实现替换它来强制纯 Python 实现。尽管这似乎是一个糟糕的 hack,但它会起作用:
import _elementtree
del _elementtree.Element
import xml.etree.ElementTree as ET
最后,您可以使用 API 的lxml
实现,ElementTree
与 stdlib 相比,它具有许多其他优点。当然它也有一些缺点,主要是你需要手动安装它(它依赖于libxml2
你可能还需要安装的 C 库)。