Pythonでマジックメソッド
『Rubyによるデザインパターン』(ラス・オルセン著 ピアソン・エデュケーション刊)の例をPythonに変換して書いています。目次
前項でBuilderパターンについて書きましたが、
ComputerBuilderを作っても、まだ、
builder.set_turbo() builder.add_dvd() builder.add_harddisk()
と、何度もメソッドを呼ばなくてはなりません。
その一つの解決策として『Rubyによるデザインパターン』では、マジックメソッドを紹介しています。
#マジックメソッドを使えば、こんなふうに書く事が出来ます。
builder.add_turbo_and_dvd_and_harddisk()
マジックメソッドは、__getattr__を使って、簡単に実装できます。
class ComputerBuilder(object): def __getattr__(self, name): words = name.split("_") if "add" != words.pop(0): return super(ComputerBuilder, self).__getattr__(name) words = [w for w in words if w != "and"] functions = dict( cd=self.add_cd, dvd=self.add_dvd, harddisk=lambda :self.add_harddisk(1024*1024*1024), turbo=self.set_turbo, ) for w in words: if w not in functions: raise AttributeError("wrong magic method word %r"%(w,)) def f(): for w in words: functions[w]() return f #使い方 builder.add_turbo_and_dvd_and_harddisk()
しかし、これは分かりにくい!!
まず、add_turbo_and_dvd_and_harddiskメソッドの実装を調べようとしたユーザーは、__getattr__のマジックの存在に気づくまで、右往左往するでしょう。
また、__getattr__が返している f がメソッドオブジェクトではなく、関数オブジェクトであるとか、docstringが付いていない等の問題もあります。、
こんな黒魔術を使うより、素直にPythonの機能を使って・・・
class ComputerBuilder(object): def add(self, items): for word in items: if word == "and": continue elif word == "cd": self.add_cd() elif word == "dvd": self.add_dvd() elif word == "harddisk": self.add_harddisk(1024*1024*1024) elif word == "turbo": self.set_turbo() else: raise ValueError("unexpected device name %r"%()) #使い方 builder.add("turbo dvd harddisk".split())
と、するか、キーワード引数を活用した方が良いと思います。