Pythonでのシングルトンの作成


946

この質問は、シングルトンデザインパターンが望ましいかどうか、アンチパターンであるかどうか、または宗教戦争の場合ではなく、このパターンがPythonで最もPython的に実装される方法について議論するためのものです。この場合、私は「ほとんどのpythonic」を「最小の驚きの原則」に従うことを定義します

シングルトンになる複数のクラスがあります(私のユースケースはロガー用ですが、これは重要ではありません)。単に継承したり装飾したりできるときに、追加されたガンフを使用していくつかのクラスを散らかしたくありません。

最良の方法:


方法1:デコレーター

def singleton(class_):
    instances = {}
    def getinstance(*args, **kwargs):
        if class_ not in instances:
            instances[class_] = class_(*args, **kwargs)
        return instances[class_]
    return getinstance

@singleton
class MyClass(BaseClass):
    pass

長所

  • デコレータは、多くの場合、複数の継承よりも直感的な方法で追加されます。

短所

  • MyClass()を使用して作成されたオブジェクトは真のシングルトンオブジェクトですが、MyClass自体はクラスではなく関数なので、そこからクラスメソッドを呼び出すことはできません。またためm = MyClass(); n = MyClass(); o = type(n)();、その後m == n && m != o && n != o

方法2:基本クラス

class Singleton(object):
    _instance = None
    def __new__(class_, *args, **kwargs):
        if not isinstance(class_._instance, class_):
            class_._instance = object.__new__(class_, *args, **kwargs)
        return class_._instance

class MyClass(Singleton, BaseClass):
    pass

長所

  • それは本当のクラスです

短所

  • 多重継承-えー!__new__2番目の基本クラスからの継承中に上書きされる可能性がありますか?必要以上に考えなければならない。

方法3:メタクラス

class Singleton(type):
    _instances = {}
    def __call__(cls, *args, **kwargs):
        if cls not in cls._instances:
            cls._instances[cls] = super(Singleton, cls).__call__(*args, **kwargs)
        return cls._instances[cls]

#Python2
class MyClass(BaseClass):
    __metaclass__ = Singleton

#Python3
class MyClass(BaseClass, metaclass=Singleton):
    pass

長所

  • それは本当のクラスです
  • 自動的に継承をカバー
  • __metaclass__その適切な目的のための使用(そして私にそれを知らせた)

短所

  • いずれかがあります?

方法4:同じ名前のクラスを返すデコレーター

def singleton(class_):
    class class_w(class_):
        _instance = None
        def __new__(class_, *args, **kwargs):
            if class_w._instance is None:
                class_w._instance = super(class_w,
                                    class_).__new__(class_,
                                                    *args,
                                                    **kwargs)
                class_w._instance._sealed = False
            return class_w._instance
        def __init__(self, *args, **kwargs):
            if self._sealed:
                return
            super(class_w, self).__init__(*args, **kwargs)
            self._sealed = True
    class_w.__name__ = class_.__name__
    return class_w

@singleton
class MyClass(BaseClass):
    pass

長所

  • それは本当のクラスです
  • 自動的に継承をカバー

短所

  • 新しいクラスを作成するためのオーバーヘッドはありませんか?ここでは、シングルトンにしたいクラスごとに2つのクラスを作成しています。私の場合はこれで問題ありませんが、これがスケーリングされないのではないかと心配しています。もちろん、このパターンをスケーリングするのがあまりにも簡単であるかどうかについては議論の余地があります...
  • _sealed属性のポイントは何ですか
  • super()再帰するため、を使用して基本クラスで同じ名前のメソッドを呼び出すことはできません。つまり__new__、を呼び出す必要があるクラスをカスタマイズしたり、サブクラス化したりすることはできません__init__

方法5:モジュール

モジュールファイル singleton.py

長所

  • 単純な方が複雑な方がいい

短所


12
別の3つの手法:代わりにモジュールを使用します(多くの場合、一般的には、これはPythonに適したパターンですが、それを使って何をしているのかによります)。単一のインスタンスを作成し、代わりにそれを処理します(foo.xまたはのFoo.x代わりに主張する場合Foo().x)。クラス属性とstatic / classメソッドを使用します(Foo.x)。
Chris Morgan、

10
@ChrisMorgan:クラス/静的メソッドのみを使用する場合は、実際にクラスを作成する必要はありません。
Cat Plus Plus

2
@Cat:効果は似ていますが、グローバル変数を作成する背後にある理由は、何もよくわかっていないなど、ほとんどすべての場合があります。なぜシングルトンを作成するのですか?あなたが尋ねなければならないなら、あなたはここにいるべきではありません。この明示性により、Pythonが強化されるだけでなく、メンテナンスがはるかに簡単になります。はい、シングルトンはグローバルの構文糖ですが、クラスは見苦しいものの束の構文糖であり、それらなしで常により良いことを誰もが言うとは思わないでしょう。
theheadofabroom

14
@BiggAl:シングルトンは、実装方法に関係なく、 Pythonicではありません。彼らはせいぜい欠陥のあるデザインの兆候です。
Cat Plus Plus

8
対抗感情は最悪の場合、カーゴカルトプログラミングです。「実際に読むのに少し気になる」と聞いた人と同じです。「Gotoステートメントは有害であると考えられています」。
Hejazzman 2015年

回答:


658

メタクラスを使用する

私はメソッド#2をお勧めしますが、基本クラスよりもメタクラスを使用する方がベターです。次に実装例を示します。

class Singleton(type):
    _instances = {}
    def __call__(cls, *args, **kwargs):
        if cls not in cls._instances:
            cls._instances[cls] = super(Singleton, cls).__call__(*args, **kwargs)
        return cls._instances[cls]

class Logger(object):
    __metaclass__ = Singleton

またはPython3で

class Logger(metaclass=Singleton):
    pass

__init__クラスが呼び出されるたびに実行したい場合は、

        else:
            cls._instances[cls].__init__(*args, **kwargs)

ifステートメントにSingleton.__call__

メタクラスについて一言。メタクラスはクラスのクラスです。つまり、クラスはそのメタクラスのインスタンスです。Pythonでオブジェクトのメタクラスを見つけるには、を使用しますtype(obj)。通常の新しいスタイルのクラスはタイプtypeです。Logger上記のコードではclass 'your_module.Singleton'、の(唯一の)インスタンスがのタイプと同じように、Loggerのタイプになりclass 'your_module.Logger'ます。あなたがロガーを呼び出すとLogger()、Pythonは最初のメタクラスを尋ねLoggerSingleton、何をすべきか、インスタンスの作成が横取りされることを可能にします。このプロセスは、を実行してクラス__getattr__の属性の1つを参照するときに、Pythonがクラスを呼び出して何を行うかを要求するのと同じですmyclass.attribute

メタクラスは基本的に、クラスの定義の意味とその定義の実装方法を決定します。たとえばhttp://code.activestate.com/recipes/498149/を参照してください。これは基本的にstruct、Pythonでメタクラスを使用してCスタイルのを再作成します。スレッドメタクラスの(具体的な)ユースケースは何ですか?また、いくつかの例も提供します。それらは一般的に、特にORMで使用されるような宣言型プログラミングに関連しているようです。

あなたが使用している場合は、このような状況では、#2の方法を、およびサブクラスが定義する__new__方法を、それがされるたびに実行され、あなたが呼び出しをSubClassOfSingleton()-それは保存されたインスタンスを返すメソッドを呼び出すための責任があるので。メタクラスを使用すると、唯一のインスタンスが作成されたときに一度だけ呼び出されます。クラスを呼び出すことの意味カスタマイズします。これは、クラスのタイプによって決まります。

一般に、メタクラスを使用してシングルトンを実装することには意味があります。シングルトンは1回だけ作成されるため特別であり、メタクラスはクラスの作成をカスタマイズする方法です。メタクラスを使用すると、他の方法でシングルトンクラス定義をカスタマイズする必要がある場合に備えてより詳細な制御が可能になります。

シングルトンには多重継承は必要ありませんが(メタクラスは基本クラスではないため)、多重継承を使用する作成されたクラスのサブクラスでは、シングルトンクラスが再定義するメタクラスを持つ最初/左端のクラスであることを確認する必要があります__call__これが問題になることはほとんどありません。インスタンスdictはインスタンスの名前空間にないため、誤って上書きすることはありません。

また、シングルトンパターンが「単一の責任の原則」に違反していることもわかります。各クラスは1つのことだけを実行する必要があります。そうすることで、別のコードに変更する必要がある場合でも、コードが1つのことを台無しにすることを心配する必要がありません。メタクラスの実装はこのテストに合格しています。メタクラスはパターン強制する責任があり、作成されたクラスとサブクラスは、それらがシングルトンであることを認識する必要はありません。「MyClass自体はクラスではなく関数なので、そこからクラスメソッドを呼び出すことはできません。」で述べたように、メソッド#1はこのテストに失敗します。

Python 2および3互換バージョン

Python2と3の両方で機能するものを作成するには、少し複雑なスキームを使用する必要があります。メタクラスは通常、タイプのサブクラスであるtypeため、メタクラスとして実行時に中間ベースクラスを動的に作成し、それをパブリックベースクラスのベースクラスとして使用することができSingletonます。次に示すように、説明するより説明するのが難しいです。

# works in Python 2 & 3
class _Singleton(type):
    """ A metaclass that creates a Singleton base class when called. """
    _instances = {}
    def __call__(cls, *args, **kwargs):
        if cls not in cls._instances:
            cls._instances[cls] = super(_Singleton, cls).__call__(*args, **kwargs)
        return cls._instances[cls]

class Singleton(_Singleton('SingletonMeta', (object,), {})): pass

class Logger(Singleton):
    pass

このアプローチの皮肉な点は、メタクラスを実装するためにサブクラス化を使用していることです。考えられる利点の1つは、純粋なメタクラスとは異なり、isinstance(inst, Singleton)が返されることTrueです。

訂正

別のトピックでは、おそらくすでにこれに気づいていますが、元の投稿の基本クラスの実装は間違っています。_instancesするニーズクラスで参照は、あなたが使用する必要がありますsuper()しているか再帰をし、__new__実際にあなたがしなければならないことを静的メソッドであるにクラスを渡し、実際のクラスのように、ではなく、クラスメソッド作成されていないとき、それはまだと呼ばれます。これらすべては、メタクラスの実装にも当てはまります。

class Singleton(object):
  _instances = {}
  def __new__(class_, *args, **kwargs):
    if class_ not in class_._instances:
        class_._instances[class_] = super(Singleton, class_).__new__(class_, *args, **kwargs)
    return class_._instances[class_]

class MyClass(Singleton):
  pass

c = MyClass()

クラスを返すデコレータ

もともとコメントを書いていましたが、長すぎたのでここに追加します。方法#4は他のデコレータバージョンよりも優れていますが、シングルトンに必要なコードよりも多く、何をするのかは明確ではありません。

主な問題は、クラスが独自の基本クラスであることから生じます。まず、クラスを、__class__属性にのみ存在する同じ名前のほぼ同一のクラスのサブクラスにするのはおかしくないですか?これは、super()再帰するため、基本クラス同じ名前のメソッドを呼び出すメソッドを定義できないことも意味します。つまり、クラスはをカスタマイズできず__new____init__呼び出しが必要なクラスから派生できません。

シングルトンパターンを使用する場合

あなたのユースケースは、シングルトンを使用したい良い例の1つです。コメントの1つで、「私にとって、ロギングは常にシングルトンの自然な候補のように思えました」と述べています。あなたは絶対に正しいです。

シングルトンが悪いと人々が言うとき、最も一般的な理由はそれらが暗黙の共有状態であるということです。グローバル変数と最上位モジュールのインポートでは、明示的な共有状態ですが、渡される他のオブジェクトは通常、インスタンス化されます。これは、2つの例外を除いて、良い点です。

最初に、そしてさまざまな場所で言及される1つは、シングルトンが一定である場合です。グローバル定数、特に列挙型の使用は広く受け入れられており、どのユーザーも他のユーザーのためにそれらを台無しにすることができないため、正気と見なされています。これは、一定のシングルトンにも同様に当てはまります。

あまり言及されない2番目の例外は逆です。シングルトンがデータシンクであり、データソースではない(直接または間接)場合です。これが、ロガーがシングルトンの「自然な」使用のように感じる理由です。さまざまなユーザーが他のユーザーが気にする方法でロガー変更していないため、実際には共有された状態はありません。これは、シングルトンパターンに対する主要な議論を無効にし、タスクの使いやすさのためにそれらを合理的な選択にします。

これはhttp://googletesting.blogspot.com/2008/08/root-cause-of-singletons.htmlからの引用です:

現在、問題のないシングルトンの1種類があります。これは、到達可能なすべてのオブジェクトが不変であるシングルトンです。すべてが一定であるため、すべてのオブジェクトが不変である場合、シングルトンはグローバルな状態を持ちません。しかし、この種のシングルトンを変更可能なものに変えるのは簡単で、非常に滑りやすい斜面です。したがって、私はこれらのシングルトンにも反対しています。それらが悪いからではなく、彼らが非常に簡単に失敗するからです。(補足として、Java列挙はこのようなシングルトンにすぎません。列挙に状態を入れない限り、問題ないので、しないでください。)

半受け入れ可能な他の種類のシングルトンは、コードの実行に影響を与えないものであり、「副作用」はありません。ロギングは完璧な例です。シングルトンとグローバル状態がロードされます。特定のロガーが有効になっているかどうかにかかわらず、アプリケーションは何の動作もしないため、これは許容できます(害を及ぼすことはありません)。ここでの情報は、アプリケーションからロガーへの1つの方法で流れます。ロガーからアプリケーションに情報が流れないため、ロガーがグローバルな状態であっても、ロガーは受け入れられます。何かがログに記録されていることをテストで確認したい場合は、ロガーを挿入する必要がありますが、一般に、ロガーは状態がいっぱいであっても害はありません。


5
いいえ、シングルトンは決して良いものではありません。ロギングは、グローバルである(恐ろしいほどひどい)の良い候補かもしれませんが、シングルトンではありません。
Cat Plus Plus

11
見てくださいgoogletesting.blogspot.com/2008/08/...。これは一般に(適切な理由で)シングルトンではありませんが、不変のシングルトンと副作用のないシングルトンが注意深く同じ問題を起こさない理由をよく説明しています。投稿の最後に少し引用します。
agf 2011

4
シングルトンに関する私の問題は、「1つのインスタンスのみ」という愚かな前提です。それとスレッドセーフティの問題のトン。そして依存関係の隠蔽。グローバルは不良であり、シングルトンはより多くの問題を抱えた単なるグローバルです。
Cat Plus Plus

4
@Catシングルトンの非常に良い使い方があります。ハードウェアモジュール(特にシングルスレッドアプリケーション)の遅延インスタンス化は、その1つです(ただし、スレッドセーフシングルトンも存在します)。
ポールマンタ2011

3
__new__メタクラスの@Alcott は、クラスが新しいときです。インスタンスが新しいときではなく、定義されたときです。クラスの呼び出し(MyClass())はオーバーライドする操作であり、クラスの定義ではありません。Pythonがどのように機能するかを本当に理解したい場合、(それを使い続ける以外に)できる最善のことはdocs.python.org/reference/datamodel.htmlを読むことです。メタクラスに関する適切なリファレンスはeli.thegreenplace.net/2011/08/14/python-metaclasses-by-exampleです。シングルトンに関する優れた記事は、この回答でリンクしたGoogleブログのシリーズです。
AGF

91
class Foo(object):
     pass

some_global_variable = Foo()

モジュールは一度だけインポートされ、他のすべては考えすぎです。シングルトンを使用せず、グローバルを使用しないようにします。


14
なぜ「シングルトンを使わない」と言ったのですか?何らかの理由?
Alcott、2011

3
シングルトンをピクルスにする必要がある場合、これは機能しません。あなたが与えた例を使用して:s = some_global_variable; str = pickle.dumps(s); s1 = pickle.loads(str); print s is s1; # False
dividebyzero

4
@dividebyzero:is演算子はポインターの等価性をテストします。pickle.loads新しく作成されたオブジェクトへの参照ではなく、既存のオブジェクトへの参照が返された場合、私はむしろそれをバグと呼んでいるほど驚いたでしょう。したがって、s is s1モジュールをシングルトンとして使用することの適合性について、テストで何もわからない場合があります。
JonasKölker、2014

1
@JonasKölker pickle.loads()は、たとえばboolおよびのインスタンスの場合、すでにそれを実行していますNoneTypepickle.loads(pickle.dumps(False)) is False利回りTrue
Dan Passaro 2014

2
@ leo-the-manic:公正なポイント。しかし、それは、オブジェクトのインターンのPythonのちょうど副作用だTrueFalseNone、コードの後ろには何の関係もありませんpickle.loads。また、読み取り専用オブジェクトに対してのみ実行しても安全です。pickle.loads既存の変更可能なオブジェクト(モジュールなど)への参照を返す場合は、バグになります。(したがって、dividebyzeroのコード例では何も証明されないという私の暗示を
支持してい

69

モジュールを使用します。一度だけインポートされます。その中にいくつかのグローバル変数を定義します-それらはシングルトンの「属性」になります。シングルトンの「メソッド」-いくつかの関数を追加します。


11
つまり、最終的には...クラスではありません。クラスとして使用することはできません。他のクラスをベースにすることはできません。インポート構文を使用すると、突然、OOPのすべての利点が失われます...
theheadofabroom

16
他のクラスをベースにすることができる場合、シングルトンではない可能性があります。派生クラスの1つだけでなく、基本クラスの1つを作成することもできますが、派生クラスも基本のメンバーであり、2つの基本があり、どちらを使用するのですか。
SingleNegationElimination 2011

これはモジュール間では機能しません。私の「メイン」モジュールで、値を設定しました。次に、それを別のモジュールとそのnullで参照します。はぁ。
ポールケンジョラ2017年

1
@PaulKenjoraコードにエラーがあるはずです。モジュールでグローバル変数を定義した場合、別のモジュールからそれにアクセスすると、値が必要です。
warvariuc

それには価値がありますが、私がそれを変更すると、それは保存されません。機能するモジュールでプロパティ付きのクラスを使用することになりました。グローバルとしての単純型は機能しませんでした(スコープが変更されるとすぐに値が失われました)。
ポールケンジョラ2017

29

Pythonではシングルトンはおそらく必要ありません。すべてのデータと関数をモジュールで定義するだけで、事実上のシングルトンができます。

シングルトンクラスが本当に必要な場合は、次のようにします。

class My_Singleton(object):
    def foo(self):
        pass

my_singleton = My_Singleton()

使用するには:

from mysingleton import my_singleton
my_singleton.foo()

ここで、mysingleton.pyはMy_Singletonが定義されているファイル名です。これは、初めてファイルがインポートされた後、Pythonがコードを再実行しないため機能します。


4
ほとんどが真実ですが、時にはそれだけでは不十分です。たとえば、DEBUGレベルで多くのクラスのインスタンスをログに記録する必要があるプロジェクトがあります。これらのクラスがインスタンス化される前にユーザー指定のログレベルを設定するために、起動時に解析されるコマンドラインオプションが必要です。モジュールレベルのインスタンス化は、これを問題にします。CLI処理が完了するまでこれらのクラスのすべてがインポートされないようにアプリを注意深く構造化することは可能ですが、アプリの自然な構造は、「シングルトンが悪い」という独断的な遵守よりも重要です。かなりきれいに行われました。
CryingCyclops 2016

my_singletonにパッチを当てているときにコードをテストするとしたら、それは可能でしょうか?このmy_singletonは他のモジュールでインスタンス化できるためです。
Naveen 2018

@Naveen-my_singletonは単一のオブジェクトです。それを「パッチ」する場合、その変更は、他のモジュールでも、将来のすべての参照に影響します。
アラン・ダイク

16

ここにあなたのためのワンライナーがあります:

singleton = lambda c: c()

使い方は次のとおりです。

@singleton
class wat(object):
    def __init__(self): self.x = 1
    def get_x(self): return self.x

assert wat.get_x() == 1

オブジェクトは熱心にインスタンス化されます。これは必要な場合とそうでない場合があります。


5
-1。これは非常に悪く、ばかげています。オブジェクトとして使用するクラスを定義しません。type(wat)またはのように醜くなければ、クラスにアクセスできなくなりますwat.__class__。本当にこれを実現したい場合は、クラスをより適切に定義してすぐにインスタンス化してください。デコレータをいじる必要はありません。
0xc0de 2014年

2
シングルトンのクラスの使用を知る必要があるのはなぜですか?ただ、シングルトンオブジェクトを使用して...
Tolli

1
シングルトンパターンではないため、IMO関数には別の名前を付ける必要があります。
GingerPlusPlus

8
ウィキペディア:「シングルトンパターンは、クラスのインスタンス化を1つのオブジェクトに制限する設計パターンです。」私の解決策はまさにそれを行うと私は言うでしょう。さて、私はそうすることができると思いますwat2 = type(wat)()が、これはpythonです、私たちはすべて大人とすべてを同意しています。インスタンスが1つしかないこと保証することはできませが、2番目のインスタンスを作成した場合、醜く、適切な場合は立ち上がった人々に、警告サインのように見えることを保証できます。何が欠けていますか?
JonasKölker、2015年

7

Stack Overflowの質問を確認するPythonでシングルトンを定義するためのシンプルでエレガントな方法はありますか?いくつかのソリューションで。

Pythonのデザインパターンに関するAlex Martelliの講演(パート1パート2)を視聴することを強くお勧めします。特に、パート1では、シングルトン/共有状態オブジェクトについて語っています。


2
これは実際には私の質問に対する回答ではありませんが、あなたが指摘するリソースは非常に役立ちます。私は不本意ながらあなたに+1を与えます
theheadofabroom '25

3

これが私自身のシングルトンの実装です。クラスを装飾するだけです。シングルトンを取得するには、Instanceメソッドを使用する必要があります。次に例を示します。

   @Singleton
   class Foo:
       def __init__(self):
           print 'Foo created'

   f = Foo() # Error, this isn't how you get the instance of a singleton

   f = Foo.Instance() # Good. Being explicit is in line with the Python Zen
   g = Foo.Instance() # Returns already created instance

   print f is g # True

そして、これがコードです:

class Singleton:
    """
    A non-thread-safe helper class to ease implementing singletons.
    This should be used as a decorator -- not a metaclass -- to the
    class that should be a singleton.

    The decorated class can define one `__init__` function that
    takes only the `self` argument. Other than that, there are
    no restrictions that apply to the decorated class.

    To get the singleton instance, use the `Instance` method. Trying
    to use `__call__` will result in a `TypeError` being raised.

    Limitations: The decorated class cannot be inherited from.

    """

    def __init__(self, decorated):
        self._decorated = decorated

    def Instance(self):
        """
        Returns the singleton instance. Upon its first call, it creates a
        new instance of the decorated class and calls its `__init__` method.
        On all subsequent calls, the already created instance is returned.

        """
        try:
            return self._instance
        except AttributeError:
            self._instance = self._decorated()
            return self._instance

    def __call__(self):
        raise TypeError('Singletons must be accessed through `Instance()`.')

    def __instancecheck__(self, inst):
        return isinstance(inst, self._decorated)

2
それは本当のシングルトンではありません。真のシングルトンでSingletonList = Singleton(list).Instance(); print(SingletonList is type(SingletonList)())印刷Trueする必要があります。コードの印刷False
GingerPlusPlus

@GingerPlusPlus私はいくつかの制限を認識していましたが、あなたが指摘した制限は認識していませんでした。言及していただきありがとうございます。残念ながら、現時点でこれに対する解決策を説明する時間はありません。
Paul Manta 14

2

方法3は非常に優れているようですが、プログラムをPython 2Python 3の両方で実行したい場合は機能しません。Python 3バージョンではPython 2で構文エラーが発生するため、Pythonバージョンのテストで個別のバリアントを保護することもできません。

マイク・ワトキンスに感謝:http : //mikewatkins.ca/2008/11/29/python-2-and-3-metaclasses/。プログラムをPython 2とPython 3の両方で動作させる場合は、次のようにする必要があります。

class Singleton(type):
    _instances = {}
    def __call__(cls, *args, **kwargs):
        if cls not in cls._instances:
            cls._instances[cls] = super(Singleton, cls).__call__(*args, **kwargs)
        return cls._instances[cls]

MC = Singleton('MC', (object), {})

class MyClass(MC):
    pass    # Code for the class implementation

割り当ての「オブジェクト」を「BaseClass」に置き換える必要があると思いますが、まだ試していません(図のようにコードを試しました)。


確かにこれはメタクラスではありません-python3ではメタクラスを使用してMyClassを構築しますclass MyClass(metaclass=Singleton)
theheadofabroom

mikewatkins.caのリンクは(事実上)壊れています。
Peter Mortensen 2016年

1

さて、モジュールレベルのグローバル化に関するPythonicの一般的な提案に同意する以外に、これはどうですか?

def singleton(class_):
    class class_w(class_):
        _instance = None
        def __new__(class2, *args, **kwargs):
            if class_w._instance is None:
                class_w._instance = super(class_w, class2).__new__(class2, *args, **kwargs)
                class_w._instance._sealed = False
            return class_w._instance
        def __init__(self, *args, **kwargs):
            if self._sealed:
                return
            super(class_w, self).__init__(*args, **kwargs)
            self._sealed = True
    class_w.__name__ = class_.__name__
    return class_w

@singleton
class MyClass(object):
    def __init__(self, text):
        print text
    @classmethod
    def name(class_):
        print class_.__name__

x = MyClass(111)
x.name()
y = MyClass(222)
print id(x) == id(y)

出力は次のとおりです。

111     # the __init__ is called only on the 1st time
MyClass # the __name__ is preserved
True    # this is actually the same instance

_sealed属性のポイントは何ですか?私が見る限り、これは何もしませんか?これについて、何かがうまくいかないと私を悩ませています...今週後半にいくつかの比較テストを実行します。
theheadofabroom '25

_sealedは、initが1回だけ実行されるようにします。通常の関数のようなデコレータよりもパフォーマンスが低下する理由はわかりません。関数はクラスごとに1回だけ実行され、新しい継承クラスを返します
Guard

ところで、編集にはインデントを解除するタブが含まれています。「2つのクラスを作成しています」とも言います-「1つの追加クラス」を作成しているということですか?
ガード

はい、1つの余分なクラスは私が意味したものです。私は__init__それが初期化されるたびに呼び出されるものを含めるつもりです。単なる 'class.methodで初期化される'だけです。インデントについては-タブとスペースを使用しました-私はそれのほとんどを修正しましたが、それを取得したい場合はそれを逃してしまったようです(編集ログを確認してください)
theheadofabroom

re init:もちろんそれはあなた次第です。他の言語でシングルトンの動作を模倣しようとしたところ、コンストラクタコード(正確にはinitではありませんが、その意味では非常に近い)は、initを毎回呼び出され、_sealed re space / tabsへのすべての参照を強制終了します。まあ、その後、emacsを修正する必要があります。とにかく、上記は修正されたバージョンです
Guard

1

これはどう:

def singleton(cls):
    instance=cls()
    cls.__new__ = cls.__call__= lambda cls: instance
    cls.__init__ = lambda self: None
    return instance

シングルトンであるべきクラスのデコレータとして使用します。このような:

@singleton
class MySingleton:
    #....

これはsingleton = lambda c: c()別の回答のデコレータに似ています。他のソリューションと同様に、唯一のインスタンスにはクラスの名前(MySingleton)があります。ただし、このソリューションでは、を実行することにより、クラスからインスタンスを「作成」することができます(実際には唯一のインスタンスを取得します)MySingleton()。またtype(MySingleton)()、同じインスタンスを返すことにより、追加のインスタンスを作成できなくなります。


オブジェクトとして使用するクラスを定義しません。
0xc0de 2014年

1
を呼び出すたびにtype(MySingleton)()MySingleton.__init__()が呼び出され、オブジェクトが複数回初期化されます。あなたはそれcls.__init__ = lambda self: passをあなたの中に書いて修正することができますsingleton。また、オーバーライドcls.__call__は意味がないように見え、有害な場合でも__call__、このコンテキストで定義されているのはMySingleton(any, list, of, arguments)、呼び出すときではなく、呼び出すときに使用されますtype(MySingleton)(any, list, of, arguments)
GingerPlusPlus

@GingerPlusPlus、__init__()実行時に再度呼び出されることを指摘していただきありがとうございtype(MySingleton)()ます。cls.__init__ = lambda self: passラムダ式の最後の部分は、ステートメントではなく式である必要があるため、提案した(を追加する)ソリューションでは構文エラーが発生します。しかし、cls.__init__ = lambda self: None作品を追加したので、それを私の答えに追加しました。
Tolli

1
@GingerPlusPlus、の使用について__call__。私の意図は、両方を作成してインスタンスtype(MySingleton)()MySingleton()返すことでした。だから、私が望んでいたことをしています。MySingletonは、シングルトンのタイプまたはシングルトンのインスタンス(あるいはその両方)のいずれかと考えることができます。
Tolli

1

私はリングに私を投げます。シンプルなデコレータです。

from abc import ABC

def singleton(real_cls):

    class SingletonFactory(ABC):

        instance = None

        def __new__(cls, *args, **kwargs):
            if not cls.instance:
                cls.instance = real_cls(*args, **kwargs)
            return cls.instance

    SingletonFactory.register(real_cls)
    return SingletonFactory

# Usage
@singleton
class YourClass:
    ...  # Your normal implementation, no special requirements.

他のいくつかのソリューションよりも優れていると思います。

  • それは明確で簡潔です(私の目には; D)。
  • その動作は完全にカプセル化されています。の実装について1つ変更する必要はありませんYourClass。これには、クラスにメタクラスを使用する必要がないことも含まれます(上のメタクラスは「実際の」クラスではなく、ファクトリーにあることに注意してください)。
  • モンキーパッチに依存しません。
  • 発信者には透過的です。
    • 呼び出し元はまだ単純にをインポートしYourClass、クラスのように見えます(そのため)、通常どおり使用します。呼び出し元をファクトリー関数に適合させる必要はありません。
    • どのようなYourClass()インスタンス化することは、まだ真のインスタンスであるYourClassあなたは、ないあらゆる種類のプロキシなので、それに起因する副作用の無いチャンスを実装しました。
    • isinstance(instance, YourClass) 同様の操作は期待どおりに機能します(ただし、このビットにはabcが必要なので、Python <2.6は除外されます)。

私には1つの欠点があります。実際のクラスのclassmethodsとstaticmethodsは、それを非表示にしているファクトリクラスを介して透過的に呼び出すことができません。これを使用することはめったにありませんが、その必要に遭遇することはありません__getattr__()が、実際のクラスにすべての属性のアクセスを委任するように実装するファクトリでカスタムメタクラスを使用することで簡単に修正できます。

私が実際に便利だと思った関連パターン(これらの種類のものが非常に頻繁に必要になると言っているわけではありません)は、同じ引数でクラスをインスタンス化すると同じインスタンスが返される「ユニーク」パターンです。つまり、「引数ごとのシングルトン」です。上記はこれにうまく適応し、さらに簡潔になります:

def unique(real_cls):

    class UniqueFactory(ABC):

        @functools.lru_cache(None)  # Handy for 3.2+, but use any memoization decorator you like
        def __new__(cls, *args, **kwargs):
            return real_cls(*args, **kwargs)

    UniqueFactory.register(real_cls)
    return UniqueFactory

とはいえ、これらのことの1つが必要だと思われる場合は、少しの間立ち止まって、本当に必要かどうか自問するべきであるという一般的なアドバイスには同意します。99%の時間、YAGNI。


1
  • 同じクラスのインスタンスを複数にしたいが、argsまたはkwargsが異なる場合のみ、これを使用できます
    1. serial通信を処理するクラスがあり、引数としてシリアルポートを送信するインスタンスを作成する場合、従来のアプローチでは機能しません
    2. 上記のデコレータを使用すると、引数が異なる場合にクラスの複数のインスタンスを作成できます。
    3. 同じ引数の場合、デコレータはすでに作成されている同じインスタンスを返します。
>>> from decorators import singleton
>>>
>>> @singleton
... class A:
...     def __init__(self, *args, **kwargs):
...         pass
...
>>>
>>> a = A(name='Siddhesh')
>>> b = A(name='Siddhesh', lname='Sathe')
>>> c = A(name='Siddhesh', lname='Sathe')
>>> a is b  # has to be different
False
>>> b is c  # has to be same
True
>>>

0

Tolliの回答に基づくコード。

#decorator, modyfies new_cls
def _singleton(new_cls):
    instance = new_cls()                                              #2
    def new(cls):
        if isinstance(instance, cls):                                 #4
            return instance
        else:
            raise TypeError("I can only return instance of {}, caller wanted {}".format(new_cls, cls))
    new_cls.__new__  = new                                            #3
    new_cls.__init__ = lambda self: None                              #5
    return new_cls


#decorator, creates new class
def singleton(cls):
    new_cls = type('singleton({})'.format(cls.__name__), (cls,), {} ) #1
    return _singleton(new_cls)


#metaclass
def meta_singleton(name, bases, attrs):
    new_cls = type(name, bases, attrs)                                #1
    return _singleton(new_cls)

説明:

  1. 指定されたものから継承して、新しいクラスを作成しますcls
    clsたとえば、誰かが望んでも変更されませんsingleton(list)

  2. インスタンスを作成します。オーバーライド__new__する前にとても簡単です。

  3. さて、インスタンスを簡単に作成したら、__new__前に定義したメソッドを使用してオーバーライドします。
  4. この関数はinstance、呼び出し元が期待する場合にのみ戻り、それ以外の場合はを発生させTypeErrorます。
    誰かが装飾されたクラスから継承しようとした場合、条件は満たされません。

  5. 場合は__new__()リターンのインスタンスclsは、新しいインスタンスの__init__()メソッドが呼び出されるように__init__(self[, ...])、自己の新しいインスタンスである場合、残りの引数が渡されたと同じです__new__()

    instanceはすでに初期化されているため、関数は__init__何もしない関数に置き換えられます。

オンラインで動作することを確認してください


0

これは、fabの回答と少し似ていますが、まったく同じではありません。

シングルトン契約は、我々はコンストラクタを複数回呼び出すことができることを必要としません。シングルトンは1回だけ作成する必要があるので、1回だけ作成されているように見えませんか?コンストラクタを「スプーフィング」すると、間違いなく読みやすさが損なわれます。

だから私の提案はこれだけです:

class Elvis():
    def __init__(self):
        if hasattr(self.__class__, 'instance'):
            raise Exception()
        self.__class__.instance = self
        # initialisation code...

    @staticmethod
    def the():
        if hasattr(Elvis, 'instance'):
            return Elvis.instance
        return Elvis()

これはinstance、ユーザーコードによるコンストラクターまたはフィールドの使用を除外しません。

if Elvis() is King.instance:

... Elvisまだ作成されていないことが確かで、作成されてKingいる場合。

しかし、それユーザーがtheメソッドを普遍的に使用することを奨励します:

Elvis.the().leave(Building.the())

これは完全にするために、あなたはまた、オーバーライドすることができます__delattr__()試みが削除するように作られている場合は例外を発生しinstance、及びオーバーライド__del__()それは例外を発生させるように(私たちは、プログラムが終了される知っている限り...)

さらなる改善


コメントと編集を手伝ってくれた方々に感謝します。私はJythonを使用していますが、これはより一般的に機能し、スレッドセーフでなければなりません。

try:
    # This is jython-specific
    from synchronize import make_synchronized
except ImportError:
    # This should work across different python implementations
    def make_synchronized(func):
        import threading
        func.__lock__ = threading.Lock()

        def synced_func(*args, **kws):
            with func.__lock__:
                return func(*args, **kws)

        return synced_func

class Elvis(object): # NB must be subclass of object to use __new__
    instance = None

    @classmethod
    @make_synchronized
    def __new__(cls, *args, **kwargs):
        if cls.instance is not None:
            raise Exception()
        cls.instance = object.__new__(cls, *args, **kwargs)
        return cls.instance

    def __init__(self):
        pass
        # initialisation code...

    @classmethod
    @make_synchronized
    def the(cls):
        if cls.instance is not None:
            return cls.instance
        return cls()

注意点:

  1. python2.xのオブジェクトからサブクラス化しない場合、使用しない古いスタイルのクラスを取得します __new__
  2. 装飾__new__するときは、@ classmethodで装飾する必要があります。そうしない__new__と、バインドされていないインスタンスメソッドになります
  3. メタクラスを使用するとthe、クラスレベルのプロパティを作成できるようになり、名前を変更できます。instance

これはシングルトンパターンのわずかに異なる解釈ですが、クラス属性に純粋に作用する__newため__init__、ではなく__ を使用したくなるかもしれませんが、これはまだ有効であると確信しています。これと方法2の違いは、複数回初期化しようとすると、単一のインスタンスが返されるか、例外が発生するかです。シングルトンパターンを満たすか、1つは使いやすいか、もう1つはシングルトンであることをより明確にすることができて嬉しいと思います。
theheadofabroom 2016

明らかにクラス名の使用__init__防止をサブクラス化するが、これは簡単に物事を行いながら、それは必須ではありません
theheadofabroom

ありがとうございます...はい、例外がスローされる前の瞬間的な2番目のインスタンス。__init__これをサブクラス化できるように変更しました...
マイク齧歯類

クール、theおそらく同様の理由のためのクラスメソッドであることから利益を得ることができる
theheadofabroom

はい、あなたが正しい。次に、SuperElvisサブクラスシングルトンと(たとえば)ImaginaryElvisサブクラスシングルトンを使用できます...それらは共存できます。追加の考えを参照してください。私のコードを自由に改善してください。
マイクげっ歯類

0

1つのライナー(私は誇りに思っていませんが、それは仕事をします):

class Myclass:
  def __init__(self):
      # do your stuff
      globals()[type(self).__name__] = lambda: self # singletonify

クラスが他のモジュールにインポートされていない限り、これでうまくいくかもしれません...
Aran-Fey

本当です。クラスを初期化するコストを支払うことができる場合、これを回避するためにクラス定義の直後にMyclass()を実行できます。
polvoazul 2018年

0

シングルトンのインスタンスの遅延初期化が必要ない場合は、次の方法が簡単でスレッドセーフになります。

class A:
    instance = None
    # Methods and variables of the class/object A follow
A.instance = A()

この方法Aは、モジュールのインポート時に初期化されたシングルトンです。


0

シングルトンパターンを誤解しているかもしれませんが、私の解決策はこのシンプルで実用的なものです(pythonic?)。このコードは2つの目標を満たします

  1. のインスタンスを作る Foo、どこでも(グローバル)accessiableを。
  2. のインスタンスは1つしかFoo存在できません。

これがコードです。

#!/usr/bin/env python3

class Foo:
    me = None

    def __init__(self):
        if Foo.me != None:
            raise Exception('Instance of Foo still exists!')

        Foo.me = self


if __name__ == '__main__':
    Foo()
    Foo()

出力

Traceback (most recent call last):
  File "./x.py", line 15, in <module>
    Foo()
  File "./x.py", line 8, in __init__
    raise Exception('Instance of Foo still exists!')
Exception: Instance of Foo still exists!

0

しばらくの間これに苦労した後、最終的に次のことを思いついたので、個別のモジュールから呼び出されたときにconfigオブジェクトが1回だけ読み込まれるようになりました。メタクラスを使用すると、グローバルクラスインスタンスを組み込みのdictに格納できます。これは現在、適切なプログラムグローバルを格納する最も簡単な方法のようです。

import builtins

# -----------------------------------------------------------------------------
# So..... you would expect that a class would be "global" in scope, however
#   when different modules use this,
#   EACH ONE effectively has its own class namespace.  
#   In order to get around this, we use a metaclass to intercept
#   "new" and provide the "truly global metaclass instance" if it already exists

class MetaConfig(type):
    def __new__(cls, name, bases, dct):
        try:
            class_inst = builtins.CONFIG_singleton

        except AttributeError:
            class_inst = super().__new__(cls, name, bases, dct)
            builtins.CONFIG_singleton = class_inst
            class_inst.do_load()

        return class_inst

# -----------------------------------------------------------------------------

class Config(metaclass=MetaConfig):

    config_attr = None

    @classmethod
    def do_load(cls):
        ...<load-cfg-from-file>...

-1

私がこの解決策をどこで見つけたのか思い出せませんが、Python専門家ではない観点から見ると、これが最も「エレガント」であることがわかりました。

class SomeSingleton(dict):
    __instance__ = None
    def __new__(cls, *args,**kwargs):
        if SomeSingleton.__instance__ is None:
            SomeSingleton.__instance__ = dict.__new__(cls)
        return SomeSingleton.__instance__

    def __init__(self):
        pass

    def some_func(self,arg):
        pass

なぜこれが好きなのですか?デコレータもメタクラスも多重継承もありません__new__。シングルトンにしたくない場合は、メソッドを削除してください。私はPython(および一般的にOOP)を初めて使用するので、これがひどいアプローチである理由を誰かが私に正しく教えてくれると思いますか?


2
なぜこれがひどいアプローチなのですか?別のシングルトンクラスを作成する場合は、をコピーして貼り付ける必要があり__new__ます。自分を繰り返さないでください
GingerPlusPlus

また、なぜあなたの新しいテイク*args**kwargs、そしてそれから何もしないのですか?これらをdict.__new__次のように渡しますdict.__new__(cls, *args, **kwargs)
GingerPlusPlus 2014

これにより__init__、クラスが呼び出されるたびにメソッドが呼び出されます。あなたの場合は__init__この方法が実際に何かをした、あなたが問題に気づくと思います。を実行するSomeSingleton()と、シングルトンの状態は__init__メソッドによってリセットされます。
Aran-Fey

-2

これは、シングルトンを実装するための私の好ましい方法です。

class Test(object):
    obj = None

    def __init__(self):
        if Test.obj is not None:
            raise Exception('A Test Singleton instance already exists')
        # Initialization code here

    @classmethod
    def get_instance(cls):
        if cls.obj is None:
            cls.obj = Test()
        return cls.obj

    @classmethod
    def custom_method(cls):
        obj = cls.get_instance()
        # Custom Code here

1
これは、クラスの複数のインスタンスが存在できるようにするため、厳密にはシングルトンではありません。改善は、クラスを初期化できないようにし、すべてのクラスメソッドがクラス属性に作用するようにすることです
theheadofabroom

-2

この答えはおそらくあなたが探しているものではありません。比較のために、そのオブジェクトだけがそのアイデンティティを持っているという意味で、シングルトンが欲しかった。私の場合、それはセンチネル値として使用されていました。答えは非常に簡単です。オブジェクトmything = object()を作成し、Pythonの性質上、そのものだけがそのアイデンティティを持ちます。

#!python
MyNone = object()  # The singleton

for item in my_list:
    if item is MyNone:  # An Example identity comparison
        raise StopIteration

モジュールを実際に複数回インポートできることを知りました。そのような場合、これはローカルシングルトンのみであり、実際にはどのような容量のシングルトンでもありません。
ThorSummoner 2018年

モジュールを複数回インポートする方法について詳しく教えてください。私が見たのは、モジュールのロード中に例外が発生したときだけです。ユーザーは後でモジュールをロードできますが、副作用が既に発生しているため、一部のアクションが再度実行される可能性があります。
sleblanc

モジュールが完全に読み込まれると、インタプリタにevalやを使用して強制する以外に、このモジュールを再度実行する方法がわかりませんimportlib.reload
sleblanc

-3

このソリューションは、モジュールレベルで名前空間の汚染を引き起こします(1つではなく3つの定義)が、私はそれを理解するのが簡単だと思います。

このようなもの(遅延初期化)を記述できるようにしたいのですが、残念ながら、独自の定義の本体でクラスを使用できません。

# wouldn't it be nice if we could do this?
class Foo(object):
    instance = None

    def __new__(cls):
        if cls.instance is None:
            cls.instance = object()
            cls.instance.__class__ = Foo
        return cls.instance

それは不可能なので、初期化と静的インスタンスを

熱心な初期化:

import random


class FooMaker(object):
    def __init__(self, *args):
        self._count = random.random()
        self._args = args


class Foo(object):
    def __new__(self):
        return foo_instance


foo_instance = FooMaker()
foo_instance.__class__ = Foo

遅延初期化:

熱心な初期化:

import random


class FooMaker(object):
    def __init__(self, *args):
        self._count = random.random()
        self._args = args


class Foo(object):
    def __new__(self):
        global foo_instance
        if foo_instance is None:
            foo_instance = FooMaker()
        return foo_instance


foo_instance = None
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.