web-dev-qa-db-ja.com

Python(Django)でのシングルトンの使用

私はPythonで次のようにシングルトンを定義することを提案されました:

class Controller:
    pass
    # ...

controller = Controller()

ただし、この方法では、controllerを派生クラスで置き換えることはできません。

Python Djangoでシングルトンを定義する他の賢明な方法は何ですか?

多分、再利用可能なアプリを定義するとき、シングルトンインスタンスをアプリで定義するのではなく、それを使用するプログラムで定義する必要がありますか?これを行う利点と欠点は何ですか?

2
porton

Pythonでのシングルトンの使用

シングルトンとは何ですか?

シングルトンは、意味的にメモリに1つだけ存在するオブジェクトです。

組み込みシングルトン

Noneは、頻繁に使用されるシングルトンです。 NotImplementedはもう1つ、使用頻度は非常に低いです。シングルトンIDをテストできます。

>>> maybe_None = None

その後

maybe_None is None

Trueを返します。

セマンティックシングルトンの作成

オブジェクトインスタンスを使用して、モジュールのグローバル名前空間に機能を必要としないインスタンスを作成できます(グローバル定数なのですべて大文字)。

SINGLETON = object() 

グローバル名前空間のオブジェクトを上書きしないように注意してください。良い名前を付けると、誰かが何か悪いことをしているときにそれが明白になります。

これは、あなたが与えた例に似ています。ただし、オブジェクトの作成に負荷がかかる場合は、私がやっていることはしません。オブジェクトを使用したくない場合でも、モジュールのインポートに負荷がかかるようになります。

インポート時にグローバルシングルトンが不要な理由

インポート時にシングルトンを作成したくない場合は、いつでもモジュールを安価にインポートする必要があります。

たとえば、ドキュメントを動的に生成するときに、モジュールを安価にインポートしたいとします。

安価なインポートを好むもう1つのケースは、単体テストの実行です。

また、使用するメソッドは継承をサポートしていません。

これを回避する方法をいくつか紹介します。

シングルトンインスタンス

クラスのインスタンスを1つだけ持つ場合は、さまざまな方法でこれを行うことができます。

__new__をカスタマイズし、クラス属性instanceを使用します

class Controller(object):
    instance = None
    def __new__(cls):
        if cls.instance is not None:
            return cls.instance
        else:
            inst = cls.instance = super(Controller, cls).__new__()
            return inst

また、これは継承をサポートすることに注意してください。

ファクトリー関数をメモする

関数をメモする関数属性を作成できます

def get_controller():
    if hasattr(get_controller, 'instance'):
        return get_controller.instance
    else:
        inst = get_controller.instance = Controller()
        return inst

モジュールまたはクラスの名前空間を使用してインスタンスを保持することもできます。これは、関数とモジュールの両方がシングルトンである必要があるためです。

または、lru_cacheを使用して(get_controllerは引数を取らないため)、それをメモします。

from functools import lru_cache

@lru_cache
def get_controller():
    return Controller()

get_controllerに引数を追加した場合、 lru_cache は、引数の一意の組み合わせごとにシングルトンを返します(キャッシュサイズまで(理論的には))。その後、インスタンスは無視されるため、注意してください。引数を追加する場合はこれで-関数属性の例で行ったように、おそらく独自の動作を実装する方が良いでしょう。

これは、たとえば、継承をサポートできます。

@lru_cache
def get_controller(controller_type): 
    if issubclass(controller_type, Controller):
        return controller_type()
    else:
        raise TypeError('must supply a Controller type')
2
Aaron Hall