Zanurkuj w Pythonie/Buforowanie odszukanego węzła

Buforowanie odszukanego węzła edytuj

kgp.py stosuje kilka sztuczek, które mogą, lecz nie muszą, być użyteczne przy przetwarzaniu XML-a. Pierwsza z nich wykorzystuje spójną strukturę dokumentów wejściowych do utworzenia bufora węzłów.

Plik gramatyki definiuje szereg elementów ref. Każdy z nich zawiera jeden lub więcej elementów p, które mogą zawierać wiele różnych rzeczy, włącznie z elementami xref. Gdy napotykamy element xref, wyszukujemy odpowiedni element ref z tym samym atrybutem id i wybieramy jeden z elementów potomnych elementu ref i parsujemy go. (W następnym podrozdziale zobaczymy jak dokonywany jest ten losowy wybór.)

W taki sposób rozwijamy gramatykę: definiujemy elementy ref dla najmniejszych części, następnie definiujemy elementy ref, które zawierają te pierwsze elementy ref poprzez użycie xref itd. Potem parsujemy "największą" referencję, przechodzimy po kolei do każdego elementu xref i ostatecznie generujemy prawdziwy tekst. Ten wygenerowany tekst zależy od tej (losowej) decyzji podjętej przy wypełnianiu elementu xref, a więc efekt może być inny za każdym razem.

To wszystko jest bardzo elastyczne, ale ma jedną wadę: wydajność. Gdy napotkamy element xref i potrzebujemy odszukać odpowiedniego dla niego elementu ref, to pojawia się problem. Element xref ma atrybut id i chcemy odszukać element ref, który ma taki sam atrybut id, ale nie ma prostego sposobu aby to zrobić. Powolnym sposobem na zrobienie tego byłoby pobranie pełnej listy elementów ref za każdym razem, a następnie przeszukanie jej w pętli pod kątem atrybutu id. Szybkim sposobem jest utworzenie takiej listy raz, a następnie utworzenie bufora w postaci słownika.

Przykład. loadGrammar
    def loadGrammar(self, grammar):                         
        self.grammar = self._load(grammar)                  
        self.refs = {}                                       #(1)
        for ref in self.grammar.getElementsByTagName("ref"): #(2)
            self.refs[ref.attributes["id"].value] = ref      #(3) (4)
  1. Rozpoczynamy od utworzenia pustego słownika self.refs.
  2. Jak już widzieliśmy w podrozdziale 9.5, “Wyszukiwanie elementów”, getElementsByTagName zwraca listę wszystkich elementów o podanej nazwie. Także łatwo możemy uzyskać listę wszystkich elementów ref, a następnie po prostu przeszukać ją w pętli.
  3. Jak już widzieliśmy w podrozdziale 9.6, "Dostęp do atrybutów elementów", możemy pobrać atrybut elementu poprzez nazwę używając standardowej składni słownikowej. Także kluczami słownika self.refs będą wartości atrybutu id każdego elementu ref.
  4. Wartościami słownika self.refs będą elementy ref jako takie. Jak już widzieliśmy w podrozdziale 9.3, "Parsowanie XML-a", każdy element, każdy węzeł, każdy komentarz, każdy kawałek tekstu w parsowanym dokumencie XML jest obiektem.

Gdy tylko bufor (cache) zostanie utworzony, po napotkaniu elementu xref, aby odnaleźć element ref z takim samym atrybutem id, możemy po prostu sięgnąć do słownika self.refs.

Przykład. Użycie bufora elementów ref
    def do_xref(self, node):
        id = node.attributes["id"].value
        self.parse(self.randomChildElement(self.refs[id]))

Funkcję randomChildElement zgłębimy w następnym podrozdziale.