[AST] (http://ja.wikipedia.org/wiki/%E6%8A%BD%E8%B1%A1%E6%A7%8B%E6%96%87%E6%9C%A8) wird untersucht [Besucher Muster] (http://ja.wikipedia.org/wiki/Visitor_%E3%83%91%E3%82%BF%E3%83%BC%E3%83%B3) kam heraus. Was hat Sie glücklich gemacht, als Sie das Besuchermuster verwendet haben? Ich kannte die Grundlagen nicht und habe sie überprüft: Schweiß:
Wikipedia hat ein Java-Codebeispiel, also habe ich es in Python3 umgeschrieben. Anfangs habe ich den japanischen Code portiert, während ich mir das Beispiel angesehen habe, und wenn ich dachte, dass es sich um ein halbfertiges Beispiel handelt, das englische Besuchermuster ) Wurde in ein prägnanteres Codebeispiel geändert. Es scheint besser, diese Seite auf Englisch zu betrachten.
Da Python keine Schnittstelle hat, verwende ich den Dekorator @ abstractmethod
des Moduls abc. Abgesehen davon gilt das Modul * abc * nur für Instanzen von Klassen, aber * zope.interface * eignet sich hervorragend für Klassen, Objekte, Module usw.
Ruhige Gesprächspause. Schauen wir uns das Muster an, während wir uns den Code ansehen.
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())
Wenn Sie den Java-Code so portieren, wie er ist, sieht er folgendermaßen aus. Da es sich jedoch um Python handelt, sollten Sie ihn etwas weiter lockern.
--- 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)
+
Um die Änderungen kurz zu erläutern, verwenden wir Reflection, um eine Standardimplementierung für etwas Interface-ähnliches zu definieren.
3.4
class ICarElementVisitor(metaclass=ABCMeta):
"""
Interface like in Python
"""
@abstractmethod
def visit(self, obj):
getattr(self, 'visit_' + obj.__class__.__name__)(obj)
3.4
class Engine(ICarElement):
def accept(self, visitor):
visitor.visit(self)
Die Besucher-Unterklasse kann direkt eine Implementierung von "visit ()" wie "PrintVisitor" definieren oder die Standardimplementierung einer abstrakten Klasse (Schnittstelle) wie "DoVisitor" verwenden.
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)
...
Das Besuchermuster übergibt das Visitor-Objekt an die Methode "accept ()" und ruft die darin enthaltene Methode "visit.visit (self)" auf (Double Dispatch). ). In diesem Beispiel wird auch die Methode "elem.accept (Besucher)" jedes Elementobjekts in "car.accept (Besucher)" aufgerufen.
Einer der Vorteile besteht darin, dass Sie über mehrere Elementobjekte hinweg arbeiten können (* traverse *), sodass Sie den Code oder die Datenstruktur vorhandener Objekte nicht ändern müssen, wenn Sie neue Operationen hinzufügen. Dies führt zu Bedenken (http://en.wikipedia.org/wiki/Separation_of_concerns).
Wenn Sie beispielsweise überlegen, eine Operation namens Bremsen hinzuzufügen,
+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')
+
Das ist alles was Sie tun müssen.
Darüber hinaus wird das "Besucher" -Objekt als leistungsfähiger als die * polymorphe * Methode beschrieben, da es auch Zustände haben kann.
In diesem Beispiel wird der Status nicht speziell verwaltet, aber es kann gesagt werden, dass es einfach ist, die Reihenfolge aufzuzeichnen, in der sie aufgerufen werden, oder ein Flag zu setzen, wenn eine Operation ausgeführt wird.
Nach dem Schreiben und erneuten Überprüfen [Vergleich von Besuchermuster und Ententypisierung] Laut (http://d.hatena.ne.jp/podhmo/20101127/1290845198), [Expert Python Programming](http://ascii.asciimw.jp/books/books/detail/978-4-04- War es in 868629-7.shtml aufgeführt)? Es war vor vielen Jahren, und ich erinnere mich, dass ich an einer Lesung teilgenommen und dort darüber gesprochen habe, aber ich habe mich überhaupt nicht an den Code erinnert.
Recommended Posts