Singletonデザインパターン
ネットで適当に調べたところ以下の2パターン見つかった。
- objectクラスの__new__スタティックメソッドをオーバーライド
- メタクラスの__call__メソッドをオーバーライド
まずはobjectの__new__をオーバーライドする方法。下記の例は、Singletonの"サブクラスのインスタンス"と"サブクラスのサブクラスのインスタンス"のid(つまりアドレス)が一致するが、『Python Cookbook 2nd Edition』に載っている例だと一致しない。理由は私自身まだ良く分かっていない。また、Singletonを継承する際はもちろん、サブクラス側で__new__をオーバーライドしてはいけない。
>>> class Singleton(object): ... instance = None ... def __new__(cls, *args): ... if cls.instance is None: ... cls.instance = object.__new__(cls, *args) ... return cls.instance ... >>> Singleton() is Singleton() True >>> class Foo(Singleton): pass ... >>> class Bar(Foo): pass ... >>> Foo() is Bar() True # ここでは、True
下記は、『Python Cookbook 2nd Edition』に載っている例。
>>> class Singleton(object): ... def __new__(cls, *args, **kwargs): ... if '_inst' not in vars(cls): ... cls._inst = super(Singleton, cls).__new__(cls, *args, **kwargs) ... return cls._inst ... >>> class Foo(Singleton): pass ... >>> class Bar(Foo): pass ... >>> Foo() is Bar() False # ここが、何故False?
次にメタクラスの__call__をオーバーライドしてカスタムメタクラスを使用する方法。
>>> class SingletonMetaclass(type): ... def __init__(cls, *args): ... type.__init__(cls, *args) ... cls.instance = None ... def __call__(cls, *args): ... if cls.instance is None: ... cls.instance = type.__call__(cls, *args) ... return cls.instance ... >>> class Singleton(object): ... __metaclass__ = SingletonMetaclass ... def __init__(self): ... print 'Initialized' ... >>> Singleton() is Singleton() Initialized True
上記の2例ではコンストラクタの引数などはここでの議論で本質的ではないので深く考えていない。また、2つの内どちらがどのように良いのかも私自身良く分かっていない。とりあえず__new__スタティックメソッドと__init__メソッドの挙動も全てきちんと把握している訳ではないので、まずはそこの理解からか…。
RubyはSingletonモジュールというのがあるみたいで、逆にPythonに標準で何故ないのかも分からない。相変わらず分からないことだらけで奥が深い。