Update 2023.11.18 2017.05.05

Python デザインパターン サンプルコード Singleton
結城 浩「Java言語で学ぶデザインパターン入門」をPython化
Python3(3.11)で動くソースコード(.pyファイル .ipynbファイル)あります
「anaconda3」on .py「PyCharm」.ipynb「Jupyter Notebook」

◆◆Singleton パターンの使われる場面◆◆

GoFによれば,こんな場面で使われるそうだ。
クラスにインスタンスが1つしか存在しないことが重要になる場合がある。たとえば,システムには多数のプリンタを接続することができるが,プリンタスプーラは1つでなければならない。同様に,ファイルシステムやウィンドウマネージャも1つでなければならない。1つのデジタルフィルタは,1つの A/D コンバータを持つだろう。また,1つの会計システムは1つの会社の専用になるだろう。

次項に書いてあるが,この「GoFの23のデザインパターン」にもいくつかシングルトンが使われることがあるそうです。


(2023-11-18)Python3.11で動作確認済み


◆◆Singleton パターンとは◆◆

GoFによれば,Singleton パターンの目的は, 「あるクラスに対してインスタンスが1つしか存在しないことを保証し,それにアクセスするためのグローバルな方法を提供する。」

GoFによれば,Singleton パターンは次のような場合に利用できる。
・クラスに対してインスタンスが1つしか存在してはならず,また,クライアントが,そのインスタンスを公開されたアクセスポイントを通してアクセスできるようにしなければならない場合。
・唯一のインスタンスがサブクラス化により拡張可能で,また,クライアントが,拡張されたインスタンスをコードの修正なしに利用できるようにしたい場合。

GoFによれば,関連するパターンは次のようなものである。
Abstract Factory パターン,Builder パターン,Prototype パターン: これらのパターンは Singleton パターンを使って実装することができる。

◆Singleton パターンの注意点

本来,クラスというものは,1つのクラスに対して複数のインスタンスを作るのが普通です。 しかし,Singleton はその真逆の使い方であり必要になる場面は希少であるので,事前の検討により本当に適用して大丈夫なのかよく吟味することが重要です。

◆◆Singleton パターンのサンプルのクラスの構成◆◆

ソースコードは巻末にあり,ソースファイルはこのWebの左上隅にありダウンロードできます。

             Singleton
            (__new__)

class Singletonは,インスタンスを1つしかつくらない特殊なクラス

◆◆Singleton パターンの実装◆◆

Javaサンプルでは,クラス定義「public class Singleton」の中のコンストラクタは

  private static Singleton singleton = new Singleton();

というようにprivate宣言しているだけです。これだけでインスタンスが1つしか存在していないことを保証しています。

Pythonの言語仕様には private という概念がないです。そこで一例として次のように実装されます。

【Singleton.pyの一部】

class Singleton(object):
    def __new__(cls, *args, **kwargs):
        if not hasattr(cls, "__instance__"):
            cls.__instance__ = \
                super(Singleton, cls).__new__(cls, *args, **kwargs)
        return cls.__instance__

Pythonでは,クラスがインスタンス化されると,最初に「__new__メソッド」が呼ばれ次に「__init__メソッド」が呼ばれます。「__init__メソッド」は他の言語ではコンストラクタと呼ばれているものです。「__new__メソッド」はPython独自の特殊メソッドです。

その使い分けは,
__new__ではインスタンスをカスタマイズし
__init__ではクラスの初期設定をする
となっているがあまり明確ではないです。

注意しなければならないのは,__new__の戻り値の書き方次第では__init__が実行されないことがあるということであります。__new__には return があり,__init__には return がありません。

上の「__new__メソッド」の内容はインスタンス化のときに,既存のインスタンスを調べて,もしあればそれをインスタンスとして戻しているだけである。これだけでインスタンスが1つしか存在していないことを保証しています。

◆Singleton パターンの他の実装

Singleton パターンの他の実装がないか調べてみました。次の2つであり,動作は検証してあります。
class Singleton(object):
    _instance = None
    def __new__(cls, *args, **kwargs):
        if not cls._instance:
            cls._instance = super(Singleton, cls).__new__(cls, *args, **kwargs)
        return cls._instance
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]
最初のものとの違いは「__instance__」を使わないで普通の変数「_instance」を型の宣言をして使っている。違いはそれだけである。変数の型によりそれに合った書き方になっている。

◆__new__のパラメータ

__new__のパラメータは3つあり,
clsはインスタンス化されるクラス名が入り,たぶん,スーパークラス(基底クラス)を呼ぶときに必要になります。
  *argsはリストまたはタプルであり呼出し時にアンパックされる。
  **kwargsは辞書dictであり呼出し時にアンパックされる。

英文のWebページでとんでもない一文をみつけた。
「*argsと**kwargsは理由が判らなくても必ず付けよ」だと。

要は,パラメータは何でも受け付けるという意思表示であろうが, 実際,クラスをインスタンス化するときに,パラメータが付いているのは明示的に__init__を呼ぶときだけです。

◆メインルーチン

Javaサンプルにならって実装してあり,クラスを2回インスタンス化して,できたオブジェクトが同じものかをチェックしています。こんな凝った書き方をしなくてもコメントアウトしてある print でインスタンスが同じであるかを直接確認することができます。

◆◆ソースコード◆◆

このWebの左上隅からダウンロードできます。

ソースファイルは1つです。
・Singleton.py;Singleton パターンのPythonサンプル

【Singleton.py】
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
#!/usr/bin/env python3
# -*- coding: utf-8 -*-

import sys

class Singleton(object):
    def __new__(cls, *args, **kwargs):
        if not hasattr(cls, "__instance__"):
            cls.__instance__ = \
                super(Singleton, cls).__new__(cls, *args, **kwargs)
        #print(cls.__instance__)
        return cls.__instance__

def main():
    sys.stdout.write("Start.\n")
    obj1 = Singleton()
    obj2 = Singleton()
    #print(obj1)
    #print(obj1)
    #print(Singleton.__instance__)
    if obj1 == obj2:
        sys.stdout.write("obj1とobj2は同じインスタンスです。\n")
    else:
        sys.stdout.write("obj1とobj2は同じインスタンスではありません。\n")
    sys.stdout.write("End.\n")

if __name__ == '__main__':
    main()

"""標準出力(comment out を外したときインスタンスが出力される)
Start.
<__main__.Singleton object at 0x05999910>
<__main__.Singleton object at 0x05999910>
<__main__.Singleton object at 0x05999910>
<__main__.Singleton object at 0x05999910>
<__main__.Singleton object at 0x05999910>
obj1とobj2は同じインスタンスです。
End.
"""

以上

トップページに戻る
inserted by FC2 system