Python デザインパターン サンプルコード State
Mark Summerfield『実践 Python 3』デザインパターンのサンプルコード
Python3(3.11)で動くソースコード(.pyファイル .ipynbファイル)あります
「anaconda3」on .py「PyCharm」.ipynb「Jupyter Notebook」
(2023-11-17)Python3.11で動作確認済み
Counter Event (__call_) () ↑ ↑ Multiplexer def generate_random_events (connect, disconnect, send)【multiplexer2.py】
Counter Event (__call_) () ↑ ↑ Multiplexer def generate_random_events (connect, disconnect, send, state)【multiplexer3.py】
Counter Event (__call_) () ↑ ↑ Multiplexer def generate_random_events (pipeline, connect, disconnect)class Multiplexer は,モードによって振る舞いを変えるメソッドを持つ。
After 100 active events: cars=150 vans=42 trucks=14 total=206 After 100 dormant events: cars=150 vans=42 trucks=14 total=206 After 100 active events: cars=303 vans=83 trucks=30 total=416
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 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116
#!/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 collections import random random.seed(917) # Not truly random for ease of regression testing def main(): totalCounter = Counter() carCounter = Counter("cars") commercialCounter = Counter("vans", "trucks") multiplexer = Multiplexer() for eventName, callback in (("cars", carCounter), ("vans", commercialCounter), ("trucks", commercialCounter)): multiplexer.connect(eventName, callback) multiplexer.connect(eventName, totalCounter) for event in generate_random_events(100): multiplexer.send(event) print("After 100 active events: cars={} vans={} trucks={} total={}" .format(carCounter.cars, commercialCounter.vans, commercialCounter.trucks, totalCounter.count)) multiplexer.state = Multiplexer.DORMANT for event in generate_random_events(100): multiplexer.send(event) print("After 100 dormant events: cars={} vans={} trucks={} total={}" .format(carCounter.cars, commercialCounter.vans, commercialCounter.trucks, totalCounter.count)) multiplexer.state = Multiplexer.ACTIVE for event in generate_random_events(100): multiplexer.send(event) print("After 100 active events: cars={} vans={} trucks={} total={}" .format(carCounter.cars, commercialCounter.vans, commercialCounter.trucks, totalCounter.count)) def generate_random_events(count): vehicles = (("cars",) * 11) + (("vans",) * 3) + ("trucks",) for _ in range(count): yield Event(random.choice(vehicles), random.randint(1, 3)) class Counter: def __init__(self, *names): self.anonymous = not bool(names) if self.anonymous: self.count = 0 else: for name in names: if not name.isidentifier(): raise ValueError("names must be valid identifiers") setattr(self, name, 0) def __call__(self, event): if self.anonymous: self.count += event.count else: count = getattr(self, event.name) setattr(self, event.name, count + event.count) class Event: def __init__(self, name, count=1): if not name.isidentifier(): raise ValueError("names must be valid identifiers") self.name = name self.count = count class Multiplexer: ACTIVE, DORMANT = ("ACTIVE", "DORMANT") def __init__(self): self.callbacksForEvent = collections.defaultdict(list) self.state = Multiplexer.ACTIVE def connect(self, eventName, callback): if self.state == Multiplexer.ACTIVE: self.callbacksForEvent[eventName].append(callback) def disconnect(self, eventName, callback=None): if self.state == Multiplexer.ACTIVE: if callback is None: del self.callbacksForEvent[eventName] else: self.callbacksForEvent[eventName].remove(callback) def send(self, event): if self.state == Multiplexer.ACTIVE: for callback in self.callbacksForEvent.get(event.name, ()): callback(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 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130
#!/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 collections import random random.seed(917) # Not truly random for ease of regression testing def main(): totalCounter = Counter() carCounter = Counter("cars") commercialCounter = Counter("trucks", "vans") multiplexer = Multiplexer() for eventName, callback in (("cars", carCounter), ("vans", commercialCounter), ("trucks", commercialCounter)): multiplexer.connect(eventName, callback) multiplexer.connect(eventName, totalCounter) for event in generate_random_events(100): multiplexer.send(event) print("After 100 active events: cars={} vans={} trucks={} total={}" .format(carCounter.cars, commercialCounter.vans, commercialCounter.trucks, totalCounter.count)) multiplexer.state = Multiplexer.DORMANT for event in generate_random_events(100): multiplexer.send(event) print("After 100 dormant events: cars={} vans={} trucks={} total={}" .format(carCounter.cars, commercialCounter.vans, commercialCounter.trucks, totalCounter.count)) multiplexer.state = Multiplexer.ACTIVE for event in generate_random_events(100): multiplexer.send(event) print("After 100 active events: cars={} vans={} trucks={} total={}" .format(carCounter.cars, commercialCounter.vans, commercialCounter.trucks, totalCounter.count)) def generate_random_events(count): vehicles = (("cars",) * 11) + (("vans",) * 3) + ("trucks",) for _ in range(count): yield Event(random.choice(vehicles), random.randint(1, 3)) class Counter: def __init__(self, *names): self.anonymous = not bool(names) if self.anonymous: self.count = 0 else: for name in names: if not name.isidentifier(): raise ValueError("names must be valid identifiers") setattr(self, name, 0) def __call__(self, event): if self.anonymous: self.count += event.count else: count = getattr(self, event.name) setattr(self, event.name, count + event.count) class Event: def __init__(self, name, count=1): if not name.isidentifier(): raise ValueError("names must be valid identifiers") self.name = name self.count = count class Multiplexer: ACTIVE, DORMANT = ("ACTIVE", "DORMANT") def __init__(self): self.callbacksForEvent = collections.defaultdict(list) self.state = Multiplexer.ACTIVE @property def state(self): return (Multiplexer.ACTIVE if self.send == self.__active_send else Multiplexer.DORMANT) @state.setter def state(self, state): if state == Multiplexer.ACTIVE: self.connect = self.__active_connect self.disconnect = self.__active_disconnect self.send = self.__active_send else: self.connect = lambda *args: None self.disconnect = lambda *args: None self.send = lambda *args: None def __active_connect(self, eventName, callback): self.callbacksForEvent[eventName].append(callback) def __active_disconnect(self, eventName, callback=None): if callback is None: del self.callbacksForEvent[eventName] else: self.callbacksForEvent[eventName].remove(callback) def __active_send(self, event): for callback in self.callbacksForEvent.get(event.name, ()): callback(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 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120
#!/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 collections import random from Qtrac import coroutine random.seed(917) # Not truly random for ease of regression testing def main(): totalCounter = Counter() carCounter = Counter("cars") commercialCounter = Counter("trucks", "vans") multiplexer = Multiplexer() pipeline = multiplexer.pipeline() for eventName, callback in (("cars", carCounter), ("vans", commercialCounter), ("trucks", commercialCounter)): multiplexer.connect(eventName, callback) multiplexer.connect(eventName, totalCounter) for event in generate_random_events(100): pipeline.send(event) print("After 100 active events: cars={} vans={} trucks={} total={}" .format(carCounter.cars, commercialCounter.vans, commercialCounter.trucks, totalCounter.count)) multiplexer.state = Multiplexer.DORMANT for event in generate_random_events(100): pipeline.send(event) print("After 100 dormant events: cars={} vans={} trucks={} total={}" .format(carCounter.cars, commercialCounter.vans, commercialCounter.trucks, totalCounter.count)) multiplexer.state = Multiplexer.ACTIVE for event in generate_random_events(100): pipeline.send(event) print("After 100 active events: cars={} vans={} trucks={} total={}" .format(carCounter.cars, commercialCounter.vans, commercialCounter.trucks, totalCounter.count)) def generate_random_events(count): vehicles = (("cars",) * 11) + (("vans",) * 3) + ("trucks",) for _ in range(count): yield Event(random.choice(vehicles), random.randint(1, 3)) class Counter: def __init__(self, *names): self.anonymous = not bool(names) if self.anonymous: self.count = 0 else: for name in names: if not name.isidentifier(): raise ValueError("names must be valid identifiers") setattr(self, name, 0) def __call__(self, event): if self.anonymous: self.count += event.count else: count = getattr(self, event.name) setattr(self, event.name, count + event.count) class Event: def __init__(self, name, count=1): if not name.isidentifier(): raise ValueError("names must be valid identifiers") self.name = name self.count = count class Multiplexer: ACTIVE, DORMANT = ("ACTIVE", "DORMANT") def __init__(self): self.callbacksForEvent = collections.defaultdict(list) self.state = Multiplexer.ACTIVE @coroutine def pipeline(self): while True: event = (yield) if self.state == Multiplexer.ACTIVE: for callback in self.callbacksForEvent.get(event.name, ()): callback(event) def connect(self, eventName, callback): if self.state == Multiplexer.ACTIVE: self.callbacksForEvent[eventName].append(callback) def disconnect(self, eventName, callback=None): if self.state == Multiplexer.ACTIVE: if callback is None: del self.callbacksForEvent[eventName] else: self.callbacksForEvent[eventName].remove(callback) 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
#!/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