lxml objectify не вызывает конструкторы для пользовательских классов элементов

lxml.objectify, похоже, не вызывает конструкторы для моих пользовательских классов элементов:

from lxml import objectify, etree

class CustomLookup(etree.CustomElementClassLookup):
    def lookup(self, node_type, document, namespace, name):
        lookupmap = { 'custom' : CustomElement }
        try:
            return lookupmap[name]
        except KeyError:
            return None

class CustomElement(etree.ElementBase):
    def __init__(self):
        print("Made CustomElement")

parser = objectify.makeparser()
parser.set_element_class_lookup(CustomLookup())
root = objectify.parse(fname,parser).getroot()

Предположим, что файл анализируется

<custom />

Я хотел бы, чтобы это напечатало "Made CustomElement", но это не так. Могу ли я заставить его вызвать конструктор?

Как можно создать экземпляр класса CustomElement без вызова конструктора?

>>> isinstance(root,CustomElement)
True

1 ответ

Решение

От lxml документы:

Инициализация элемента

Есть одна вещь, которую нужно знать заранее. Классы элементов не должны иметь __init___ или же __new__ метод. Также не должно быть никакого внутреннего состояния, за исключением данных, хранящихся в базовом дереве XML. Экземпляры элементов создаются и мусор собирается при необходимости, поэтому невозможно предсказать, когда и как часто для них создается прокси. Еще хуже, когда __init__ вызывается метод, объект еще даже не инициализирован для представления тега XML, поэтому в предоставлении __init__ метод в подклассах.

В большинстве случаев не требуется инициализация класса, поэтому вы можете довольствоваться переходом к следующему разделу. Однако, если вам действительно нужно настроить класс элементов при создании экземпляра, есть один из возможных способов сделать это. Классы ElementBase имеют _init() метод, который может быть переопределен. Его можно использовать для изменения дерева XML, например, для создания специальных дочерних элементов или проверки и обновления атрибутов.

Семантика _init() являются следующими:

  • Он вызывается один раз во время создания класса Element. То есть, когда представление элемента в Python создается с помощью lxml. В это время объект элемента полностью инициализируется для представления конкретного элемента XML в дереве.

  • Метод имеет полный доступ к дереву XML. Модификации могут быть сделаны точно так же, как и в любом другом месте программы.

  • Представления элементов в Python могут создаваться несколько раз за время существования XML-элемента в базовом дереве C. _init() Код, предоставляемый подклассами, должен сам по себе особо заботиться о том, чтобы множественные исполнения были либо безвредными, либо чтобы им препятствовал какой-то флаг в дереве XML. Последнее может быть достигнуто путем изменения значения атрибута или путем удаления или добавления определенного дочернего узла, а затем проверки этого перед выполнением процесса инициализации.

  • Любые исключения, поднятые в _init() будет распространяться через вызов API, который приведет к созданию элемента. Так что будьте осторожны с кодом, который вы пишете здесь, поскольку его исключения могут появляться в разных неожиданных местах.

Другие вопросы по тегам