Python デザインパターン サンプルコード Bridge
Mark Summerfield『実践 Python 3』デザインパターンのサンプルコード
Python3(3.11)で動くソースコード(.pyファイル .ipynbファイル)あります
「anaconda3」on .py「PyCharm」.ipynb「Jupyter Notebook」
(2023-11-16)Python3.11で動作確認済み
【重要な注意】本プログラムはファイルを出力します。その使い方は不明です。
「PyCharm」上に「Jupyter Notebook」上に出力されるのは出力ファイルのディレクトリだけです。そのディレクトリは「os.path」で指定しています。
wrote C:\Users\yamak\AppData\Local\Temp\Forecast_6_8.xpm
wrote C:\Users\yamak\AppData\Local\Temp\Forecast_6_8.gif
wrote C:\Users\yamak\AppData\Local\Temp\Forecast_6_8.xpm
「AppData」は隠しフォルダであり,
エクスプローラ(ユーザーアカウント)→表示→表示→隠しファイル
により,エクスプローラに他のフォルダのように表示されます。
上のディレクりのフォルダの中にはたくさんのファイルが有りますが,更新日時でソートすれば1番目に見つかります。
BarCharter BarRenderer (render) (initialize, draw_caption, draw_bar, finalize) ↑ ↑ ↑ TextBarRenderer ImageBarRenderer (追加機能はここに) (do.) (do.)class BarCharter は,機能側の上位クラスです。 Bridge の役割をします。
Forecast 6/8 ============ ************************** Mon **************************** Tue ******************************* Wed ************************************ Thu **************************************** Fri *********************************** Sat ******************************* Sun
23 24 25 26 27 28 29
def main(): pairs = (("Mon", 16), ("Tue", 17), ("Wed", 19), ("Thu", 22), ("Fri", 24), ("Sat", 21), ("Sun", 19)) textBarCharter = BarCharter(TextBarRenderer()) textBarCharter.render("Forecast 6/8", pairs) imageBarCharter = BarCharter(ImageBarRenderer()) imageBarCharter.render("Forecast 6/8", pairs)
barchart1.py と同じです
barchart1.py と同じです
32 33 34
class BarRenderer(Qtrac.Requirer): required_methods = {"initialize", "draw_caption", "draw_bar", "finalize"}
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
#!/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 os import re import tempfile import Qtrac try: import cyImage as Image except ImportError: import Image def main(): pairs = (("Mon", 16), ("Tue", 17), ("Wed", 19), ("Thu", 22), ("Fri", 24), ("Sat", 21), ("Sun", 19)) textBarCharter = BarCharter(TextBarRenderer()) textBarCharter.render("Forecast 6/8", pairs) imageBarCharter = BarCharter(ImageBarRenderer()) imageBarCharter.render("Forecast 6/8", pairs) @Qtrac.has_methods("initialize", "draw_caption", "draw_bar", "finalize") class BarRenderer(metaclass=abc.ABCMeta): pass class BarCharter: def __init__(self, renderer): if not isinstance(renderer, BarRenderer): raise TypeError("Expected object of type BarRenderer, got {}". format(type(renderer).__name__)) self.__renderer = renderer def render(self, caption, pairs): maximum = max(value for _, value in pairs) self.__renderer.initialize(len(pairs), maximum) self.__renderer.draw_caption(caption) for name, value in pairs: self.__renderer.draw_bar(name, value) self.__renderer.finalize() class TextBarRenderer: def __init__(self, scaleFactor=40): self.scaleFactor = scaleFactor def initialize(self, bars, maximum): assert bars > 0 and maximum > 0 self.scale = self.scaleFactor / maximum def draw_caption(self, caption): print("{0:^{2}}\n{1:^{2}}".format(caption, "=" * len(caption), self.scaleFactor)) def draw_bar(self, name, value): print("{} {}".format("*" * int(value * self.scale), name)) def finalize(self): pass class ImageBarRenderer: COLORS = [Image.color_for_name(name) for name in ("red", "green", "blue", "yellow", "magenta", "cyan")] def __init__(self, stepHeight=10, barWidth=30, barGap=2): self.stepHeight = stepHeight self.barWidth = barWidth self.barGap = barGap def initialize(self, bars, maximum): assert bars > 0 and maximum > 0 self.index = 0 color = Image.color_for_name("white") self.image = Image.Image(bars * (self.barWidth + self.barGap), maximum * self.stepHeight, background=color) def draw_caption(self, caption): self.filename = os.path.join(tempfile.gettempdir(), re.sub(r"\W+", "_", caption) + ".xpm") def draw_bar(self, name, value): color = ImageBarRenderer.COLORS[self.index % len(ImageBarRenderer.COLORS)] width, height = self.image.size x0 = self.index * (self.barWidth + self.barGap) x1 = x0 + self.barWidth y0 = height - (value * self.stepHeight) y1 = height - 1 self.image.rectangle(x0, y0, x1, y1, fill=color) self.index += 1 def finalize(self): self.image.save(self.filename) print("wrote", self.filename) 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
#!/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 os import re import tempfile import tkinter as tk import Qtrac def main(): pairs = (("Mon", 16), ("Tue", 17), ("Wed", 19), ("Thu", 22), ("Fri", 24), ("Sat", 21), ("Sun", 19)) textBarCharter = BarCharter(TextBarRenderer()) textBarCharter.render("Forecast 6/8", pairs) imageBarCharter = BarCharter(ImageBarRenderer()) imageBarCharter.render("Forecast 6/8", pairs) @Qtrac.has_methods("initialize", "draw_caption", "draw_bar", "finalize") class BarRenderer(metaclass=abc.ABCMeta): pass class BarCharter: def __init__(self, renderer): self.__renderer = renderer def render(self, caption, pairs): maximum = max(value for _, value in pairs) self.__renderer.initialize(len(pairs), maximum) self.__renderer.draw_caption(caption) for name, value in pairs: self.__renderer.draw_bar(name, value) self.__renderer.finalize() class TextBarRenderer: def __init__(self, scaleFactor=40): self.scaleFactor = scaleFactor def initialize(self, bars, maximum): assert bars > 0 and maximum > 0 self.scale = self.scaleFactor / maximum def draw_caption(self, caption): print("{0:^{2}}\n{1:^{2}}".format(caption, "=" * len(caption), self.scaleFactor)) def draw_bar(self, name, value): print("{} {}".format("*" * int(value * self.scale), name)) def finalize(self): pass class ImageBarRenderer: COLORS = ("red", "green", "blue", "yellow", "magenta", "cyan") def __init__(self, stepHeight=10, barWidth=30, barGap=2): self.stepHeight = stepHeight self.barWidth = barWidth self.barGap = barGap def initialize(self, bars, maximum): assert bars > 0 and maximum > 0 if tk._default_root is None: self.gui = tk.Tk() self.inGui = False else: self.gui = tk._default_root self.inGui = True self.index = 0 self.width = bars * (self.barWidth + self.barGap) self.height = maximum * self.stepHeight self.image = tk.PhotoImage(width=self.width, height=self.height) self.image.put("white", (0, 0, self.width, self.height)) def draw_caption(self, caption): self.filename = os.path.join(tempfile.gettempdir(), re.sub(r"\W+", "_", caption) + ".gif") def draw_bar(self, name, value): color = ImageBarRenderer.COLORS[self.index % len(ImageBarRenderer.COLORS)] x0 = self.index * (self.barWidth + self.barGap) x1 = x0 + self.barWidth y0 = self.height - (value * self.stepHeight) y1 = self.height - 1 self.image.put(color, (x0, y0, x1, y1)) self.index += 1 def finalize(self): self.image.write(self.filename, "gif") print("wrote", self.filename) if not self.inGui: self.gui.quit() 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
#!/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 os import re import tempfile import Qtrac try: import cyImage as Image except ImportError: import Image def main(): pairs = (("Mon", 16), ("Tue", 17), ("Wed", 19), ("Thu", 22), ("Fri", 24), ("Sat", 21), ("Sun", 19)) textBarCharter = BarCharter(TextBarRenderer()) textBarCharter.render("Forecast 6/8", pairs) imageBarCharter = BarCharter(ImageBarRenderer()) imageBarCharter.render("Forecast 6/8", pairs) class BarRenderer(Qtrac.Requirer): required_methods = {"initialize", "draw_caption", "draw_bar", "finalize"} class BarCharter: def __init__(self, renderer): if not isinstance(renderer, BarRenderer): raise TypeError("Expected object of type BarRenderer, got {}". format(type(renderer).__name__)) self.__renderer = renderer def render(self, caption, pairs): maximum = max(value for _, value in pairs) self.__renderer.initialize(len(pairs), maximum) self.__renderer.draw_caption(caption) for name, value in pairs: self.__renderer.draw_bar(name, value) self.__renderer.finalize() class TextBarRenderer: def __init__(self, scaleFactor=40): self.scaleFactor = scaleFactor def initialize(self, bars, maximum): assert bars > 0 and maximum > 0 self.scale = self.scaleFactor / maximum def draw_caption(self, caption): print("{0:^{2}}\n{1:^{2}}".format(caption, "=" * len(caption), self.scaleFactor)) def draw_bar(self, name, value): print("{} {}".format("*" * int(value * self.scale), name)) def finalize(self): pass class ImageBarRenderer: COLORS = [Image.color_for_name(name) for name in ("red", "green", "blue", "yellow", "magenta", "cyan")] def __init__(self, stepHeight=10, barWidth=30, barGap=2): self.stepHeight = stepHeight self.barWidth = barWidth self.barGap = barGap def initialize(self, bars, maximum): assert bars > 0 and maximum > 0 self.index = 0 color = Image.color_for_name("white") self.image = Image.Image(bars * (self.barWidth + self.barGap), maximum * self.stepHeight, background=color) def draw_caption(self, caption): self.filename = os.path.join(tempfile.gettempdir(), re.sub(r"\W+", "_", caption) + ".xpm") def draw_bar(self, name, value): color = ImageBarRenderer.COLORS[self.index % len(ImageBarRenderer.COLORS)] width, height = self.image.size x0 = self.index * (self.barWidth + self.barGap) x1 = x0 + self.barWidth y0 = height - (value * self.stepHeight) y1 = height - 1 self.image.rectangle(x0, y0, x1, y1, fill=color) self.index += 1 def finalize(self): self.image.save(self.filename) print("wrote", self.filename) if __name__ == "__main__": main()
46 47 48 49 50 51 52 53 54 55 56 57 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91
def has_methods(*methods): def decorator(Base): def __subclasshook__(Class, Subclass): if Class is Base: attributes = collections.ChainMap(*(Superclass.__dict__ for Superclass in Subclass.__mro__)) if all(method in attributes for method in methods): return True return NotImplemented Base.__subclasshook__ = classmethod(__subclasshook__) return Base return decorator class Requirer(metaclass=abc.ABCMeta): # Since we have rules for adding new expected attributes, we *do* # perform the check for subclasses @classmethod def __subclasshook__(Class, Subclass): methods = set() for Superclass in Subclass.__mro__: if hasattr(Superclass, "required_methods"): methods |= set(Superclass.required_methods) attributes = collections.ChainMap(*(Superclass.__dict__ for Superclass in Class.__mro__)) if all(method in attributes for method in methods): return True return NotImplemented