Singletonパターン
結城浩さんの「Java言語で学ぶデザインパターン入門」を題材にpythonでデザインパターンを書いてみる。今回の題材はSingletonパターン。指定したクラスのインスタンスが絶対に1つしか存在しないことを保証するデザインパターン。
pythonでこのデザインパターンを実装する方法として特殊メソッド__new__()をオーバーライドする方法があるようなので、まずは
クラスを定義するときに使う __new__(cls[,args....]) という特殊メソッドについて勉強。
pythonでのクラス定義では、普通は__init__メソッドで初期化を行うけど生成過程を制御したいときには__new__メソッドを
使うらしい。
とりあえずPython日本語マニュアルなどで調べてみた。
__new__メソッドも__init__メソッドと同じように引数を設定してクラスオブジェクトの関数の呼び出しの形で使うみたい。
ただ__init__メソッドが戻り値を設定しないのに対して__new__メソッドは戻り値を設定できるらしい。つまり自分自身である
インスタンスオブジェクト以外のものを返すことができるということ。
また__new__メソッドはクラスメソッドなので第一引数はインスタンスではなくクラスとなるらしい。
クラス cls の新しいインスタンスを作るために呼び出されて、残りの引数はオブジェクトのコンストラクタの式 (クラスの呼び出し文) に渡されるらしい。それで __new__() の戻り値は新しいオブジェクトのインスタンス (通常は cls のインスタンス) でなければならないらしい。
__new__() が cls のインスタンスを返した場合、 "__init__(self[, ...])" のようにしてインスタンスの __init__() が呼び出されると。このとき、self は新たに生成されたインスタンスで、残りの引数は __new__() に渡された引数と同じになるらしい。
今、新しいスタイルのクラス Cls があって、クラス Cls の新しいインスタンスをつくるために Cls(*args, *kwargs) とすると、
Pythonでは Cls.__new__(Cls, *args, **kwargs) が呼ばれるらしい。
__new__メソッドは戻り値 x を指定できるが、その戻り値が新しくつくられたインスタンスとして使われるらしい。
つづいてPythonは Cls.__init__(x, *args, **kwargs)を呼ぶけど、これは x が実際に Cls のインスタンスである場合に限られるらしい。それ以外の場合は x の状態は__new__が残した状態のままとなるらしい。
object.__new__とすると、第一引数として受け取るクラスから新しいインスタンスを初期化されていない状態でつくるらしい。
ここまでをふまえて、実装コードを見てみると、
class Singleton(object): _instances = dict() def __new__(cls, *args, **kwargs): if cls not in cls._instances: cls._instances[cls] = object.__new__(cls, *args, **kwargs) return cls._instances[cls] class DerivedSingleton(Singleton): pass if __name__ == '__main__': print id(Singleton()) print id(Singleton()) print id(DerivedSingleton()) print id(DerivedSingleton())
う〜ん。よくわからない。
実行結果は、
12483248 12483248 12483280 12483280
ふむ。確かにインスタンスの識別値が同じになってる。
ただ、いまいち__new__メソッドが完全に理解できてないのでまた勉強しなければ。