这里的根本问题是它xml.etree.ElementTree.Element不是为子类而设计的。
我不认为这是故意的,他们只是没想到有人会继承它,也没有考虑过。在 Python 3 中,您用纯 Python 编写的几乎所有内容都可以很好地进行子类化,但 C-API 类是另一回事。如果您查看xml.etree.ElementTree.Element,它实际上是_elementtree.Element,它是用 C 实现的(作为cElementTree2.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.ElementET.ElementAttributeErrorET.Elementint
但:
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 库)。