Python デザインパターン サンプルコード Chain of Responsibility
Mark Summerfield『実践 Python 3』デザインパターンのサンプルコード
Python3(3.11)で動くソースコード(.pyファイル .ipynbファイル)あります
「anaconda3」on .py「PyCharm」.ipynb「Jupyter Notebook」
(2023-11-17)Python3.11で動作確認済み
NullHandler (handle) ↑ ↑ ↑ ↑ DebugHandler MouseHandler KeyHandler TimerHandler (handle) (handle) (handle) (handle)class NullHandler は,スーパークラスです。
def debug_handler def mouse_handler def key_handler def timer_handlereventhandler1.py のクラスに対応するコルーチンがあります。
Handler Chain #1 Press: Key Shift+b Click: Button 3 (584, 427) Press: Key i Press: Key Shift+m Press: Key Shift+m Press: Key t Press: Key Ctrl+Shift+f Click: Button 3 (255, 91) Click: Button 2 (49, 112) Press: Key Ctrl+p Click: Button 2 (189, 125) Press: Key k Click: Button 2 (239, 286) Handler Chain #2 (debugging) *DEBUG*: Timer 0 Timeout: Timer 0 *DEBUG*: Timer 1 Timeout: Timer 1 *DEBUG*: Button 2 (105, 144) Click: Button 2 (105, 144) *DEBUG*: Timer 2 Timeout: Timer 2 *DEBUG*: Key r Press: Key r *DEBUG*: Key Ctrl+s Press: Key Ctrl+s *DEBUG*: Key z Press: Key z *DEBUG*: Key Shift+j Press: Key Shift+j *DEBUG*: Key Shift+j Press: Key Shift+j *DEBUG*: Key y Press: Key y *DEBUG*: Key x Press: Key x
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86
#!/usr/bin/env python3 # Copyright c 2012-13 Qtrac Ltd. All rights reserved. # This program or module is free software: you can redistribute it # and/or modify it under the terms of the GNU General Public License as # published by the Free Software Foundation, either version 3 of the # License, or (at your option) any later version. It is provided for # educational purposes and is distributed in the hope that it will be # useful, but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # General Public License for more details. import sys import Event def main(): print("Handler Chain #1") handler1 = TimerHandler(KeyHandler(MouseHandler(NullHandler()))) # Could pass None or nothing instead of the NullHandler while True: event = Event.next() if event.kind == Event.TERMINATE: break handler1.handle(event) print("\nHandler Chain #2 (debugging)") handler2 = DebugHandler(handler1) while True: event = Event.next() if event.kind == Event.TERMINATE: break handler2.handle(event) class NullHandler: def __init__(self, successor=None): self.__successor = successor def handle(self, event): if self.__successor is not None: self.__successor.handle(event) class DebugHandler(NullHandler): def __init__(self, successor=None, file=sys.stdout): super().__init__(successor) self.__file = file def handle(self, event): self.__file.write("*DEBUG*: {}\n".format(event)) super().handle(event) class MouseHandler(NullHandler): def handle(self, event): if event.kind == Event.MOUSE: print("Click: {}".format(event)) else: super().handle(event) class KeyHandler(NullHandler): def handle(self, event): if event.kind == Event.KEYPRESS: print("Press: {}".format(event)) else: super().handle(event) class TimerHandler(NullHandler): def handle(self, event): if event.kind == Event.TIMER: print("Timeout: {}".format(event)) else: super().handle(event) if __name__ == "__main__": main()
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74
#!/usr/bin/env python3 # Copyright c 2012-13 Qtrac Ltd. All rights reserved. # This program or module is free software: you can redistribute it # and/or modify it under the terms of the GNU General Public License as # published by the Free Software Foundation, either version 3 of the # License, or (at your option) any later version. It is provided for # educational purposes and is distributed in the hope that it will be # useful, but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # General Public License for more details. import sys import Event from Qtrac import coroutine def main(): print("Handler Chain #1") pipeline = key_handler(mouse_handler(timer_handler())) while True: event = Event.next() if event.kind == Event.TERMINATE: break pipeline.send(event) print("\nHandler Chain #2 (debugging)") pipeline = debug_handler(pipeline) while True: event = Event.next() if event.kind == Event.TERMINATE: break pipeline.send(event) @coroutine def debug_handler(successor, file=sys.stdout): while True: event = (yield) file.write("*DEBUG*: {}\n".format(event)) successor.send(event) @coroutine def mouse_handler(successor=None): while True: event = (yield) if event.kind == Event.MOUSE: print("Click: {}".format(event)) elif successor is not None: successor.send(event) @coroutine def key_handler(successor=None): while True: event = (yield) if event.kind == Event.KEYPRESS: print("Press: {}".format(event)) elif successor is not None: successor.send(event) @coroutine def timer_handler(successor=None): while True: event = (yield) if event.kind == Event.TIMER: print("Timeout: {}".format(event)) elif successor is not None: successor.send(event) if __name__ == "__main__": main()
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60
#!/usr/bin/env python3 # Copyright c 2012-13 Qtrac Ltd. All rights reserved. # This program or module is free software: you can redistribute it # and/or modify it under the terms of the GNU General Public License as # published by the Free Software Foundation, either version 3 of the # License, or (at your option) any later version. It is provided for # educational purposes and is distributed in the hope that it will be # useful, but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # General Public License for more details. import random import string random.seed(917) # Don't want random for regression tests MOUSE, KEYPRESS, TIMER, TERMINATE = ("MOUSE", "KEYPRESS", "TIMER", "TERMINATE") def next(): kinds = ([MOUSE] * 7) + ([KEYPRESS] * 11) + ([TIMER] * 5) + [TERMINATE] kind = random.choice(kinds) if kind == MOUSE: return Event(kind, button=random.randint(1, 3), x=random.randint(0, 640), y=random.randint(0, 480)) elif kind == KEYPRESS: return Event(kind, ctrl=random.randint(1, 7) == 1, shift=random.randint(1, 5) == 1, key=random.choice(string.ascii_lowercase)) return Event(kind) # TIMER or TERMINATE class Event: TimerId = 0 def __init__(self, kind, **kwargs): assert kind in {MOUSE, KEYPRESS, TIMER, TERMINATE} self.kind = kind self.kwargs = kwargs if self.kind == TIMER: self.kwargs["id"] = Event.TimerId Event.TimerId += 1 def __str__(self): if self.kind == MOUSE: return "Button {} ({}, {})".format( self.kwargs.get("button", 1), self.kwargs.get("x", -1), self.kwargs.get("y", -1)) elif self.kind == KEYPRESS: return "Key {}{}{}".format( "Ctrl+" if self.kwargs.get("ctrl", False) else "", "Shift+" if self.kwargs.get("shift", False) else "", self.kwargs.get("key", "")) elif self.kind == TIMER: return "Timer {}".format(self.kwargs.get("id", -1)) elif self.kind == TERMINATE: return "Terminate"
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
#!/usr/bin/env python3 # Copyright c 2012-13 Qtrac Ltd. All rights reserved. # This program or module is free software: you can redistribute it # and/or modify it under the terms of the GNU General Public License as # published by the Free Software Foundation, either version 3 of the # License, or (at your option) any later version. It is provided for # educational purposes and is distributed in the hope that it will be # useful, but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # General Public License for more details. import abc import collections import errno import functools import os import sys def coroutine(function): @functools.wraps(function) def wrapper(*args, **kwargs): generator = function(*args, **kwargs) next(generator) return generator return wrapper