IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
10.5. Créer des gestionnaires distincts pour chaque type de noeud

10.5. Créer des gestionnaires distincts pour chaque type de noeud

Une troisième astuce bien utile au traitement XML implique la séparation de votre code en fonctions logiques, sur la base des types de noeud et des noms d'élément. Les documents XML analysés sont constitués de divers types de noeud, chacun représenté par un objet Python. La racine d'un document est elle-même représentée par un objet Document. L'objet Document contient alors un ou plusieurs objets Element (pour les balises XML courantes), dont chacun peut contenir d'autres objets Element, Text (pour les fragments de texte), ou Comment (pour les commentaires imbriqués). Python facilite l'écriture d'un sélecteur pour séparer la logique de chaque type de noeud.

Exemple 10.17. Les noms de classe des objets XML analysés

>>> from xml.dom import minidom

>>> xmldoc = minidom.parse('kant.xml') 1
>>> xmldoc
<xml.dom.minidom.Document instance at 0x01359DE8>
>>> xmldoc.__class__                   2
<class xml.dom.minidom.Document at 0x01105D40>
>>> xmldoc.__class__.__name__          3
'Document'
1 Supposez pour le moment que kant.xml se trouve dans le répertoire courant.
2 Comme vous l'avez vu dans la Section 9.2, «Les paquetages», l'objet retourné par l'analyse d'un document XML est un objet Document, tel que défini dans le module minidom.py du paquetage xml.dom. Comme vous l'avez vu dans la Section 5.4, «Instantiation de classes», __class__ est un attribut prédéfini de chaque objet Python.
3 De plus, __name__ est un attribut prédéfini de chaque classe Python et c'est une chaîne. Cette chaîne n'a rien de mystérieux; elle correspond au nom de la classe que vous inscrivez lorsque vous définissez vous-même une classe. (Voir la Section 5.3, «Définition de classes».)

Parfait, vous pouvez désormais obtenir le nom de la classe de n'importe quel noeud XML particulier (puisque chaque noeud XML est représenté par un objet Python). Comment pouvez-vous mettre cet avantage à profit pour séparer la logique de traitement de chaque type de noeud ? La réponse est getattr, ce que vous aviez vu précédemment dans la Section 4.4, «Obtenir des références objet avec getattr».

Exemple 10.18. La fonction parse, un sélecteur de noeuds XML générique

    def parse(self, node):          
        parseMethod = getattr(self, "parse_%s" % node.__class__.__name__) 1 2
        parseMethod(node) 3
1 Tout d'abord, remarquez que vous construisez une longue chaîne basée sur le nom de la classe du noeud que vous passez à la fonction (dans l'argument node). Ainsi, si vous passez un noeud Document, vous constituez la chaîne 'parse_Document' et ainsi de suite.
2 Maintenant vous pouvez traiter cette chaîne comme un nom de fonction et obtenir une référence de la fonction elle-même en utilisant getattr
3 Enfin, vous pouvez appeler cette fonction et lui passer le noeud comme argument. L'exemple suivant présente les définitions de chacune de ces fonctions.

Exemple 10.19. Les fonctions appelées par le sélecteur de méthodes parse

    def parse_Document(self, node): 1
        self.parse(node.documentElement)

    def parse_Text(self, node):    2
        text = node.data
        if self.capitalizeNextWord:
            self.pieces.append(text[0].upper())
            self.pieces.append(text[1:])
            self.capitalizeNextWord = 0
        else:
            self.pieces.append(text)

    def parse_Comment(self, node): 3
        pass

    def parse_Element(self, node): 4
        handlerMethod = getattr(self, "do_%s" % node.tagName)
        handlerMethod(node)
1 La fonction parse_Document n'est jamais appelée qu'une fois, puisqu'il n'y a qu'un unique noeud Document dans un document XML et un unique objet Document dans la représentation du document XML analysé. Elle ne tient compte que de l'élément racine du fichier de grammaire et l'analyse.
2 La fonction parse_Text est appelée pour les noeuds de type texte. Elle commence par mettre automatiquement en majuscule le premier mot de chaque phrase et ajoute ensuite le texte considéré à une liste.
3 La fonction parse_Comment ne contient que l'instruction pass, puisque vous n'avez que faire des commentaires imbriqués dans les fichiers de grammaire. Notez, cependant, que vous avez tout de même besoin de définir cette fonction et, explicitement, de ne lui faire jouer aucun rôle. Si cette fonction n'existait pas, la fonction générique parse échouerait sitôt qu'elle rencontrerait un commentaire, faute de pouvoir trouver la fonction parse_Comment. Définir une fonction distincte pour chaque type de noeud, même si elle n'est d'aucun usage, permet à la fonction générique parse de rester simple et silencieuse.
4 La méthode parse_Element est en réalité elle-même un sélecteur, basé sur le nom de la balise de l'élément. L'idée de départ est la même : retenir le caractère discriminant de chacun de ces éléments (le nom de leur balise) et l'affecter à une fonction distincte. Vous construisez une chaîne comme 'do_xref' (pour une balise <xref>), trouvez la fonction qui porte ce nom et l'appelez. Et ainsi de suite pour chacun des autres noms de balise que vous pourriez trouver au cours de l'analyse d'un fichier de grammaire (les balises <p>, les balises <choice>).

Dans cet exemple, les fonctions de sélection parse et parse_Element trouvent simplement les autres méthodes dans la même classe. Si votre traitement est très complexe (ou si vous avez de nombreux noms de balise), vous pourriez diviser votre code en modules séparés et utiliser l'importation dynamique pour importer chaque module et appeler les fonctions dont vous avez besoin. L'importation dynamique sera discuté dans le Chapitre 16, Programmation fonctionnelle.