这里的根本问题是
xml.etree.ElementTree.Element
没有被设计成可继承的。我认为这不是故意的,他们只是没有预料到会有人去继承它,并且也没有考虑过这个问题。在Python 3中,几乎所有用纯Python编写的东西都可以很好地进行子类化,但是C-API类就不同了。如果你看一下
xml.etree.ElementTree.Element
,它实际上是
_elementtree.Element
,而
_elementtree.Element
是
使用C语言实现的(作为2.x版 cElementTree 的简单移植)。
让我们来看一个简化版本以了解这个问题:
import xml.etree.ElementTree as ET
class KML(ET.Element):
def __init__(self, *args):
super().__init__('kml')
k = KML()
k.doc = 'Doc'
当您尝试分配给“k.doc”时,它会立即引发“AttributeError”。为什么?这是因为调用了“__setattr__”,而您和“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__
或
__init__
内部时,它会被吞噬,使问题更难以调试。
那么,你该怎么办呢?
一种可能性是自己实现
__setattr__
。
这不适用于所有非子类友好的C-API类,但在这种情况下,您实际上拥有一个适当的
__dict__
,默认(
object
)实现的
__setattr__
和相关函数使用它,只是您没有这个实现。
您可以进行monkeypatching,或者尝试设置适当的多重继承(但是
Element
由于与原始问题类似的原因而会遇到问题)。
但我认为最简单的方法是明确地编写它:
def __setattr__(self, attr, value):
self.__dict__[attr] = value
def __delattr__(self, attr):
del self.__dict__[attr]
另一种可能性是通过阻止C实现替换纯Python实现来强制使用纯Python实现。虽然这似乎是一个不好的hack,但它可以工作:
import _elementtree
del _elementtree.Element
import xml.etree.ElementTree as ET
最后,您可以使用lxml
实现的ElementTree
API,它比stdlib的实现有许多其他优点。当然,它也有一些缺点,其中主要的一个是您需要手动安装它(并且它依赖于C库libxml2
,您可能还需要安装它)。
super()
两次?你是不是想要执行self.append(...)
? - shx2k = KML()
是从另一个文件中调用的,但我认为这并不重要。@shx2 我在代码中稍后定义了另一个append
,我想运行ET.Element
的append而不是KML
的。 - mwillseyElementTree
(因为在3.2+中只要可以导入C实现,就会自动发生),这种方式不允许您以这种方式附加额外的属性。如果您尝试在__init__
或__new__
方法之外的任何地方使用k.doc = 'Doc'
,则将在该行上获得AttributeError
。在__init__
或__new__
内部,它不会引发错误,但也不会执行任何操作,因此第一次尝试访问self.doc
将引发错误。 - abarnert