Modèle de visiteur en Python

[AST] Je recherche dans (http://ja.wikipedia.org/wiki/%E6%8A%BD%E8%B1%A1%E6%A7%8B%E6%96%87%E6%9C%A8) [Visiteur modèle] (http://ja.wikipedia.org/wiki/Visitor_%E3%83%91%E3%82%BF%E3%83%BC%E3%83%B3) est sorti. Qu'est-ce qui vous a rendu heureux lorsque vous avez utilisé le modèle Visiteur? Je ne connaissais pas les bases, alors je l'ai revu: sueur:

wikipedia a un exemple de code Java, donc je l'ai réécrit en Python3. Au départ, je portais le code japonais en regardant l'exemple, et si je pensais que c'était un échantillon à moitié fini, le [modèle de visiteur] anglais (http://en.wikipedia.org/wiki/Visitor_pattern) ) A été modifié en un exemple de code plus concis. Il semble préférable de regarder cette page en anglais.

Python n'a pas d'interface, j'utilise donc le décorateur @ abstractmethod du module abc. En passant, le module * abc * ne s'applique qu'aux instances de classes, mais * zope.interface * est idéal pour les classes, les objets, les modules, etc.

Pause de conversation tranquille. Regardons le modèle tout en regardant le code.

3.4


# -*- coding: utf-8 -*-
from abc import ABCMeta, abstractmethod

class ICarElementVisitor(metaclass=ABCMeta):
    """
    Interface like in Python
    """
    @abstractmethod
    def visit_Wheel(self, wheel): pass

    @abstractmethod
    def visit_Engine(self, engine): pass

    @abstractmethod
    def visit_Body(self, body): pass

    @abstractmethod
    def visit_Car(self, car): pass

class ICarElement(metaclass=ABCMeta):
    """
    Interface like in Python
    """
    @abstractmethod
    def accept(self, visitor): pass

class Wheel(ICarElement):
    def __init__(self, name):
        self.name = name

    def accept(self, visitor):
        visitor.visit_Wheel(self)

class Engine(ICarElement):
    def accept(self, visitor):
        visitor.visit_Engine(self)

class Body(ICarElement):
    def accept(self, visitor):
        visitor.visit_Body(self)

class Car(ICarElement):

    def __init__(self):
        self.elements = [
            Wheel('front left'), Wheel('front right'),
            Wheel('back left'), Wheel('back right'),
            Body(), Engine(),
        ]

    def accept(self, visitor):
        for elem in self.elements:
            elem.accept(visitor)
        visitor.visit_Car(self)

class PrintVisitor(ICarElementVisitor):
    def visit_Wheel(self, wheel):
        print('Visiting {} wheel'.format(wheel.name))

    def visit_Engine(self, engine):
        print('Visiting engine')

    def visit_Body(self, body):
        print('Visiting body')

    def visit_Car(self, car):
        print('Visiting car')

class DoVisitor(ICarElementVisitor):
    def visit_Wheel(self, wheel):
        print('Kicking my {} wheel'.format(wheel.name))

    def visit_Engine(self, engine):
        print('Starting my engine')

    def visit_Body(self, body):
        print('Moving my body')

    def visit_Car(self, car):
        print('Starting my car')

def main():
    """
    >>> main()
    Visiting front left wheel
    Visiting front right wheel
    Visiting back left wheel
    Visiting back right wheel
    Visiting body
    Visiting engine
    Visiting car
    --------------------------------
    Kicking my front left wheel
    Kicking my front right wheel
    Kicking my back left wheel
    Kicking my back right wheel
    Moving my body
    Starting my engine
    Starting my car
    """
    car = Car()
    car.accept(PrintVisitor())
    print('-' * 32)
    car.accept(DoVisitor())

Si vous portez le code Java tel quel, il ressemble à ceci, mais comme il s'agit de Python, lâchons-le un peu plus.

--- visitor.py	2015-02-17 18:43:53.000000000 +0900
+++ visitor-generic.py	2015-02-17 18:46:24.000000000 +0900
@@ -6,16 +6,8 @@
     Interface like in Python
     """
     @abstractmethod
-    def visit_Wheel(self, wheel): pass
-
-    @abstractmethod
-    def visit_Engine(self, engine): pass
-
-    @abstractmethod
-    def visit_Body(self, body): pass
-
-    @abstractmethod
-    def visit_Car(self, car): pass
+    def visit(self, obj):
+        getattr(self, 'visit_' + obj.__class__.__name__)(obj)
 
 class ICarElement(metaclass=ABCMeta):
     """
@@ -29,15 +21,15 @@
         self.name = name
 
     def accept(self, visitor):
-        visitor.visit_Wheel(self)
+        visitor.visit(self)
 
 class Engine(ICarElement):
     def accept(self, visitor):
-        visitor.visit_Engine(self)
+        visitor.visit(self)
 
 class Body(ICarElement):
     def accept(self, visitor):
-        visitor.visit_Body(self)
+        visitor.visit(self)
 
 class Car(ICarElement):
 
@@ -51,9 +43,12 @@
     def accept(self, visitor):
         for elem in self.elements:
             elem.accept(visitor)
-        visitor.visit_Car(self)
+        visitor.visit(self)
 
 class PrintVisitor(ICarElementVisitor):
+    def visit(self, obj):
+        getattr(self, 'visit_' + obj.__class__.__name__)(obj)
+
     def visit_Wheel(self, wheel):
         print('Visiting {} wheel'.format(wheel.name))
 
@@ -67,6 +62,9 @@
         print('Visiting car')
 
 class DoVisitor(ICarElementVisitor):
+    def visit(self, obj):
+        super().visit(obj)
+

Pour expliquer brièvement les changements, nous utilisons la réflexion pour définir une implémentation par défaut pour quelque chose de type interface.

3.4


class ICarElementVisitor(metaclass=ABCMeta):
    """
    Interface like in Python
    """
    @abstractmethod
    def visit(self, obj):
        getattr(self, 'visit_' + obj.__class__.__name__)(obj)

Ensuite, la classe qui reçoit le visiteur peut être unifiée pour appeler visiteur.visit (self), ce qui est un peu plus propre.

3.4


class Engine(ICarElement):
    def accept(self, visitor):
        visitor.visit(self)

La sous-classe Visiteur peut définir directement une implémentation de «visit ()», telle que «PrintVisitor», ou elle peut utiliser l'implémentation par défaut d'une classe abstraite (interface), telle que «DoVisitor».

3.4


class PrintVisitor(ICarElementVisitor):
    def visit(self, obj):
        getattr(self, 'visit_' + obj.__class__.__name__)(obj)
    ...

class DoVisitor(ICarElementVisitor):
    def visit(self, obj):
        super().visit(obj)
    ...

Le pattern Visitor passe l'objet Visitor à la méthode ʻaccept () et appelle la méthode visiteur.visit (self) à l'intérieur ([Double Dispatch](http://en.wikipedia.org/wiki/Double_dispatch)). ). Cet exemple appelle également la méthode ʻelem.accept (visiteur) de chaque objet élément à l'intérieur decar.accept (visiteur).

L'un des avantages est que vous pouvez opérer sur plusieurs objets élément (* traverse *) afin de ne pas avoir à modifier le code ou la structure de données des objets existants lorsque vous ajoutez de nouvelles opérations. Cela suscite des inquiétudes (http://en.wikipedia.org/wiki/Separation_of_concerns).

Par exemple, si vous pensez ajouter une opération appelée freinage,

+class Brake(ICarElement):
+    def accept(self, visitor):
+        visitor.visit(self)
+
 class Car(ICarElement):
 
     def __init__(self):
         self.elements = [
             Wheel('front left'), Wheel('front right'),
             Wheel('back left'), Wheel('back right'),
-            Body(), Engine(),
+            Body(), Engine(), Brake(),
         ]
@@ -55,6 +59,9 @@
     def visit_Engine(self, engine):
         print('Visiting engine')
 
+    def visit_Brake(self, engine):
+        print('Visiting brake')
+        

C'est tout ce que vous devez faire.

De plus, l'objet visiteur est décrit comme étant plus puissant que la méthode * polymorphe * car il peut aussi avoir des états.

Dans cet exemple, l'état n'est pas géré en particulier, mais on peut dire qu'il est facile d'enregistrer l'ordre dans lequel ils sont appelés, ou de mettre un drapeau lorsqu'une opération est effectuée.

Après l'avoir écrit et réexaminé, [Comparaison du modèle de visiteur et du typage du canard] Selon (http://d.hatena.ne.jp/podhmo/20101127/1290845198), [Expert Python Programming](http://ascii.asciimw.jp/books/books/detail/978-4-04- Était-il répertorié dans 868629-7.shtml)? C'était il y a de nombreuses années, et je me souviens avoir assisté à une séance de lecture et en avoir parlé là-bas, mais je ne me souvenais pas du tout du code.

Recommended Posts

Modèle de visiteur en Python
Motif singleton en Python
Apprenez le modèle de conception "Visiteur" avec Python
Implémenter le modèle Singleton en Python
Quadtree en Python --2
Python en optimisation
CURL en Python
Métaprogrammation avec Python
Python 3.3 avec Anaconda
Géocodage en python
SendKeys en Python
Méta-analyse en Python
Unittest en Python
Époque en Python
Discord en Python
Allemand en Python
DCI en Python
tri rapide en python
nCr en python
N-Gram en Python
Programmation avec Python
Plink en Python
Constante en Python
FizzBuzz en Python
Sqlite en Python
Étape AIC en Python
LINE-Bot [0] en Python
CSV en Python
Assemblage inversé avec Python
Réflexion en Python
Constante en Python
nCr en Python.
format en python
Scons en Python 3
Puyopuyo en python
python dans virtualenv
PPAP en Python
Quad-tree en Python
Réflexion en Python
Chimie avec Python
Hashable en Python
DirectLiNGAM en Python
LiNGAM en Python
Aplatir en Python
Aplatir en python
Apprenez le modèle de conception "Prototype" avec Python
Apprenez le modèle de conception "Builder" avec Python
Apprenez le modèle de conception "Observer" en Python
Apprenez le modèle de conception "Proxy" en Python
Apprenez le modèle de conception "Commande" en Python
Apprenez le modèle de conception "Bridge" avec Python
Apprenez le modèle de conception "Mediator" avec Python
Apprenez le modèle de conception "Décorateur" avec Python
Apprenez le modèle de conception "Iterator" avec Python
Apprenez le modèle de conception «Stratégie» avec Python
Apprenez le modèle de conception "Composite" avec Python
Apprenez le modèle de conception "État" en Python
Apprenez le modèle de conception "Adapter" avec Python
Liste triée en Python