Коренная проблема заключается в том, что xml.etree.ElementTree.Element
не предназначен для подкласса.
Я не думаю, что это было намеренно, они просто не ожидали, что кто-нибудь подклассифицирует его, и не думал об этом в любом случае. В Python 3 почти все, что вы пишете на чистом Python, прекрасно подклассифицируется, но класс C-API - это совсем другая история. И если вы посмотрите на xml.etree.ElementTree.Element
, это фактически _elementtree.Element
, что составляет implemented in 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__
, который ни вы, ни 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__
и друзей использовать, вы просто надеваете» У меня есть такая реализация.
Вы можете обезвредить его или попытаться настроить правильное многократное наследование (но у Element
возникнут проблемы с тем же причинам, что и исходная проблема).
Но я думаю, что это гораздо проще просто написать его в явном виде:
def __setattr__(self, attr, value):
self.__dict__[attr] = value
def __delattr__(self, attr):
del self.__dict__[attr]
Другая возможность заключается в том, чтобы заставить реализацию чисто Python, препятствуя реализации C от замены. Хотя это кажется плохой хак, он будет работать:
import _elementtree
del _elementtree.Element
import xml.etree.ElementTree as ET
Наконец, вы можете использовать lxml
реализацию ElementTree
API, который имеет ряд других преимуществ по сравнению с STDLIB один. Конечно, у него также есть некоторые недостатки, главный из которых заключается в том, что вам необходимо вручную установить его (и это зависит от библиотеки C libxml2
, которую также может потребоваться установить).
Пожалуйста, опубликуйте все сообщение об ошибке со всей трассировкой стека. – BrenBarn
Почему вы вызываете 'super()' дважды? вы хотели сделать 'self.append (...)'? – shx2
@BrenBarn Вы можете просто запустить это в интерпретаторе, и он отбросит ошибку атрибута без реальной трассировки стека. В моем более длинном коде 'k = KML()' вызывается из другого файла, но я не думаю, что это актуально. @ shx2 Я определяю другое 'append' позже в коде, и я хочу запускать' ET.Element', а не 'KML'. – mwillsey