0utputab1e

[ デザインパターン ] PythonでSingletonパターンを実装する

PythonでSingletonパターンを実装してみた。

Singletonパターンとは

アプリケーション全体で唯一のインスタンスになることを保証するデザインパターンのこと。

アプリケーション全体で状態を共有する必要があるようなケースで役に立つ実装パターンらしい。

実装

以下のように「__new__()」メソッド(インスタンス作成時に必ず実行されるメソッド)を使って、アプリケーション全体で共有できるような実装をしてみる。

ちなみに「cls」とはインスタンスの生成で必要となるクラス自身のことを指す。

class Singleton:
    def __new__(cls, *args):
        if not hasattr(cls, "_self"):
            cls._self = super().__new__(cls)
        return cls._self

class SubCls(Singleton);
    def __init__(self, prop1):
        self.prop1 = prop1

if __name__=="__main__":
    sub1 = SubCls("sub1")
    print("sub1 = %s" % sub1.prop1)
    sub2 = SubCls("sub2")
    print("sub1 = %s, sub2 = %s" % (sub1.prop1, sub2.prop1))
    sub3 = SubCls("sub3")
    print("sub1 = %s, sub2 = %s, sub3 = %s" % (sub1.prop1, sub2.prop1, sub3.prop1))

実行

$ python singleton_app.py
sub1 = sub1
sub1 = sub2, sub2 = sub2
sub1 = sub3, sub2 = sub3, sub3 = sub3

解説

「__init__()」はインスタンス生成時に必ず呼び出されるメソッドである。

初回の呼び出しでは「super().__new__(cls)」で自身クラスをもとにインスタンスを生成し戻り値として返すが、その際クラスメンバ変数にインスタンスを保存しておく(グローバル変数のようなものかも)。

次回以降インスタンス作成時は、先程のクラスメンバ変数を確認し、そこにあるインスタンスを「__new__()」メソッドからそのまま返すことで、既存インスタンスを利用する。

そのため、インスタンス初期化時に渡したプロパティがアプリケーション全体のインスタンスで共有される。

「__init__()」メソッドで初期化用引数を受け取るために、「__new__()」メソッドでも可変長引数が受け入れられるように「*args」を含めておかないと

Traceback (most recent call last):
  File "singleton2.py", line 12, in <module>
    sub1 = SubCls("sub1")
TypeError: __new__() takes 1 positional argument but 2 were given

のように怒られるので注意。

最後に

Pythonのコードは比較的見やすいので、デザインパターンに限らずこれからの学習/調査で活用していきたいです。

その上で他の言語にも応用できればうれしいです。

 

あわせて読みたい記事

>> Homeに戻る