Zanurkuj w Pythonie/Tworzenie oddzielnych funkcji obsługi względem typu węzła: Różnice pomiędzy wersjami

Usunięta treść Dodana treść
Piotr (dyskusja | edycje)
Nie podano opisu zmian
Piotr (dyskusja | edycje)
przetłumaczone
Linia 2:
== Tworzenie oddzielnych funkcji obsługi względem typu węzła ==
 
Trzecim użytecznym chwytem podczas przetwarzania XML-a jest podzielenie kodu w logiczny sposób na funkcje oparte na typie węzła i nazwie elementu. Parsując dokument przetwarzamy rozmaite typy węzłów, które są reprezentowane przez obiekty Pythona. Poziom główny dokumentu jest bezpośrednio reprezentowany przez obiekt dokumentu<tt>Document</tt>. NastępnieZ dokumentkolei <tt>Document</tt> zawiera jeden lub więcej obiektów elementówklasy <tt>Element</tt> (w rzeczywistości znaczników XML), a każdy z nich może zawierać inne obiekty elementówklasy <tt>Element</tt>, obiektówobiekty tekstowychklasy <tt>Text</tt> (fragmenty tekstu), czy obiektów komentarzy<tt>Comment</tt> (osadzone komentarze w dokumencie). Python pozwala w łatwy sposób napisać funkcję pośredniczącą, która rozdziela logikę dla każdego rodzaju węzła.
 
'''Przykład 10.17. Nazwy klas parsowanych obiektów XML'''
Linia 16:
 
# Załóżmy na moment, że <tt>kant.xml</tt> jest w bieżącym katalogu.
# Jak powiedzieliśmy w ''podrozdziale 9.2'', “Pakiety”, obiekt zwrócony przez parsowany dokument jest obiekteminstancją dokumentuklasy <tt>Document</tt>, któryktóra zostałzostała zdefiniowanyzdefiniowana w <tt>minidom.py</tt> w pakiecie <tt>xml.dom</tt> package. AsJak youzobaczyliśmy saww in Section''Podrozdziale 5.4'', “Instantiating“Tworzenie Classes”instancji klasy”, <tt>__class__</tt> is built-injest attributewbudowanym ofatrybutem everykażdego Pythonobiektu objectPythona.
# Ponadto, <tt>__name__</tt> jest wbudowanym atrybutem każdej klasy Pythona. Atrybut ten przechowuje napis, a napis ten nie jest niczym tajemniczym, jest po prostu nazwą danej klasy. (Zobacz ''podrozdział 5.3'', “Definiowanie klas”.)
# Furthermore, __name__ is a built-in attribute of every Python class, and it is a string. This string is not mysterious; it's the same as the class name you type when you define a class yourself. (See Section 5.3, “Defining Classes”.)
 
To fajnie, możemy pobrać nazwę klasy dowolnego węzła XML-a (ponieważ węzły są reprezentowane przez Pythonowe obiekty). Jak można wykorzystać tę zaletę, aby rozdzielić logikę parsowania dla każdego typu węzła? Odpowiedzią jest <tt>getattr</tt>, który pierwszy raz zobaczyliśmy w ''podrozdziale 4.4'', “Pobieranie referencji do obiektu”.
Fine, so now you can get the class name of any particular XML node (since each XML node is represented as a Python object). How can you use this to your advantage to separate the logic of parsing each node type? The answer is getattr, which you first saw in Section 4.4, “Getting Object References With getattr”.
 
'''ExamplePrzykład 10.18. <tt>parse</tt>, aogólna genericfunkcja XMLpośrednicząca nodedla dispatcherwęzła XML'''
 
def parse(self, node):
parseMethod = getattr(self, "parse_%s" % node.__class__.__name__) #(1) (2)
parseMethod(node) #(3)
 
# Od razu, zauważ, że konstruujemy dłuższy napis oparty na nazwie klasy przekazanego węzła (jako argument <tt>node</tt>). Zatem, jeśli przekażemy węzeł <tt>Document</tt>-u, konstruujemy napis <tt>'parse_Document'</tt> itd.
# First off, notice that you're constructing a larger string based on the class name of the node you were passed (in the node argument). So if you're passed a Document node, you're constructing the string 'parse_Document', and so forth.
# Teraz, jeśli potraktujemy tę nazwę jako nazwę funkcji, otrzymamy dzięki <tt>getattr</tt> referencję do funkcji.
# Now you can treat that string as a function name, and get a reference to the function itself using getattr
# Ostatecznie, możemy wywołać tę funkcję, przekazując sam <tt>node</tt> jako argument. Następny przykład przedstawia definicję tych funkcji.
# Finally, you can call that function and pass the node itself as an argument. The next example shows the definitions of each of these functions.
 
'''ExamplePrzykład 10.19. FunctionsFunkcje calledwywoływane byprzez thefunkcję parsepośredniczącą dispatcher<tt>parse</tt>'''
 
def parse_Document(self, node): #(1)
self.parse(node.documentElement)
 
def parse_Text(self, node): #(2)
text = node.data
Linia 52:
handlerMethod(node)
 
# <tt>parse_Document</tt> isjest onlywywołany evertylko called onceraz, sinceponieważ therejest istylko onlyjeden onewęzeł klasy <tt>Document</tt> nodew in andokumencie XML document,i andtylko onlyjeden oneobiekt klasy <tt>Document</tt> objectw inprzeparsowanej the parsedreprezentacji XML representation-a. It simply turnsTu aroundpo andprostu parsesidziemy thedalej rooti elementparsujemy ofczęść thegłówną grammarpliku filegramatyki.
# <tt>parse_Text</tt> jest wywoływany tylko na węzłach reprezentujących fragmenty tekstu. Funkcja wykonuje kilka specjalnych operacji związanych z automatycznym wstawianiem dużej litery na początku słowa pierwszego zdania, ale w innym wypadku po prostu dodaje reprezentowany tekst do listy.
# parse_Text is called on nodes that represent bits of text. The function itself does some special processing to handle automatic capitalization of the first word of a sentence, but otherwise simply appends the represented text to a list.
# <tt>parse_Comment</tt> jest tylko "przejażdżką"; metoda ta nic nie robi, ponieważ nie musimy się troszczyć o komentarze wstawione w plikach definiującym gramatykę. Pomimo tego, zauważ, że nadal musimy zdefniować funkcję i wyraźnie stwierdzić, żeby nic nie robiła. Jeśli funkcja nie będzie istniała, funkcja <tt>parse</tt> nawali tak szybko, jak napotka się na komentarz, ponieważ będzie próbowała znaleźć nieistniejącą funkcję <tt>parse_Comment</tt>. Definiując oddzielną funkcję dla każdego typu węzła, nawet jeśli nam ta funkcja nie jest potrzebna, pozwalamy ogólnej funkcji parsującej być prostą i krótką.
# parse_Comment is just a pass, since you don't care about embedded comments in the grammar files. Note, however, that you still need to define the function and explicitly make it do nothing. If the function did not exist, the generic parse function would fail as soon as it stumbled on a comment, because it would try to find the non-existent parse_Comment function. Defining a separate function for every node type, even ones you don't use, allows the generic parse function to stay simple and dumb.
# Metoda <tt>parse_Element</tt> jest faktycznie funkcją pośredniczącą, opartą na nazwie znacznika elementu. Idea jest taka sama: weź odróżniające się od siebie elementy (elementy, które różnią się nazwą znacznika) i wyślij je do odpowiedniej, odrębnej funkcji. Konstruujemy napis typu <tt>'do_xref'</tt> (dla znacznika <tt><xref></tt>), znajdujemy funkcję o takiej nazwie i wywołujemy ją. I tak dalej, dla każdego innej nazwy znacznika, która zostanie znaleziona, oczywiście w pliku gramatyki (czyli znaczniki <tt><nowiki><p></nowiki></tt>, czy też <tt><choice></tt>).
# The parse_Element method is actually itself a dispatcher, based on the name of the element's tag. The basic idea is the same: take what distinguishes elements from each other (their tag names) and dispatch to a separate function for each of them. You construct a string like 'do_xref' (for an <xref> tag), find a function of that name, and call it. And so forth for each of the other tag names that might be found in the course of parsing a grammar file (<p> tags, <choice> tags).
 
W tym przykładzie funkcja pośrednicząca <tt>parse</tt> i <tt>parse_Element</tt> po prostu znajdują inne metody w tej samej klasie. Jeśli przetwarzanie jest bardzo złożone (lub mamy bardzo dużo nazw znaczników), powinieneś rozdzielić swój kod na kilka oddzielnych modułów i wykorzystać dynamiczne importowanie, aby zaimportować każdy moduł, a następnie wywołać wszystkie potrzebne nam funkcje. Dynamiczne importowanie zostanie omówione w ''Rozdziale 16'', ''Programowanie funkcyjne''.
In this example, the dispatch functions parse and parse_Element simply find other methods in the same class. If your processing is very complex (or you have many different tag names), you could break up your code into separate modules, and use dynamic importing to import each module and call whatever functions you needed. Dynamic importing will be discussed in Chapter 16, Functional Programming.
 
<noinclude>