3

我正在将我的 KML 库从 python 2 更改为 3,我在这里遇到了障碍。每当创建 KML 对象时k = KML(),例如,我都会收到以下错误:AttributeError: 'KML' object has no attribute 'doc'参考给定代码的最后一行。我不知道这里发生了什么,因为我已经明确定义了属性。这是代码。

import xml.etree.ElementTree as ET

class KML(ET.Element):
    def __init__(self):
        super().__init__("kml")
        self.doc = ET.Element("Doc")
        super().append(self.doc)     #error points here

任何帮助将不胜感激。谢谢!

4

1 回答 1

3

这里的根本问题是它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 库)。

于 2013-05-24T18:40:43.033 回答