ホームページ >バックエンド開発 >Python チュートリアル >Python でシングルトン パターンを作成する 5 つの一般的な方法
いわゆるシングルトンとは、クラスのインスタンスが最初から最後まで 1 回しか作成できないことを意味します。
方法 1
クラスに最初から最後まで最大 1 つのインスタンスを持たせたい場合は、__new__ メソッドを使用するのが非常に簡単です。 Python のクラスは、__new__ を通じてインスタンスを作成します:
class Singleton(object): def __new__(cls,*args,**kwargs): if not hasattr(cls,'_inst'): cls._inst=super(Singleton,cls).__new__(cls,*args,**kwargs) return cls._inst if __name__=='__main__': class A(Singleton): def __init__(self,s): self.s=s a=A('apple') b=A('banana') print id(a),a.s print id(b),b.s
結果:
29922256 Banana
29922256 Banana
__new__ メソッドを使用して、クラスのインスタンスが作成されたときにそのクラスのインスタンスをバインドします。 cls._inst が None の場合、クラスがインスタンス化されていないことを意味し、インスタンスを cls._inst にバインドします。最初のインスタンス化で作成されたインスタンスは、今後インスタンス化されるたびに返されます。シングルトンからサブクラスを派生するときは、__new__ をオーバーロードしないように注意してください。
方法 2:
場合によっては、生成されたインスタンスが同じ ID を持つかどうかは気にせず、そのステータスと動作だけを気にすることがあります。多数のインスタンスの作成を許可できますが、すべての
class Borg(object): _shared_state={} def __new__(cls,*args,**kwargs): obj=super(Borg,cls).__new__(cls,*args,**kwargs) obj.__dict__=cls._shared_state return obj
は、すべてのインスタンスの __dict__ が同じ辞書を指すようにするため、インスタンスは同じメソッドとプロパティを共有します。インスタンスの name 属性の設定は、__init__ 内で変更されたか直接変更されたかに関係なく、すべてのインスタンスに影響します。ただし、インスタンス ID は異なります。クラス インスタンスが属性を共有できるが、サブクラスとは共有できないようにするには、Borg._shared_state の代わりに cls._shared_state を必ず使用してください。
インスタンスの ID が異なるため、各インスタンスを辞書キーとして使用できます:
if __name__=='__main__': class Example(Borg): pass a=Example() b=Example() c=Example() adict={} j=0 for i in a,b,c: adict[i]=j j+=1 for i in a,b,c: print adict[i]
結果:
0
1
2
この動作が希望どおりでない場合は、次のようにすることができます。 __eq__ メソッドと __hash__ メソッドを Borg クラスに追加して、シングルトン パターンの動作に近づけます。
class Borg(object): _shared_state={} def __new__(cls,*args,**kwargs): obj=super(Borg,cls).__new__(cls,*args,**kwargs) obj.__dict__=cls._shared_state return obj def __hash__(self): return 1 def __eq__(self,other): try: return self.__dict__ is other.__dict__ except: return False if __name__=='__main__': class Example(Borg): pass a=Example() b=Example() c=Example() adict={} j=0 for i in a,b,c: adict[i]=j j+=1 for i in a,b,c: print adict[i]
結果:
2
2
2
キーが使用されている場合、すべてのインスタンスが可能です。
方法 3
クラスを作成すると、いくつかのメカニズムがクラス名、基本クラスのタプル、クラス ディクショナリを使用してクラス オブジェクトを作成します。新しいクラスのこのメカニズムはデフォルトで type になり、このメカニズムはプログラム可能であり、メタクラス __metaclass__ と呼ばれます。
class Singleton(type): def __init__(self,name,bases,class_dict): super(Singleton,self).__init__(name,bases,class_dict) self._instance=None def __call__(self,*args,**kwargs): if self._instance is None: self._instance=super(Singleton,self).__call__(*args,**kwargs) return self._instance if __name__=='__main__': class A(object): __metaclass__=Singleton a=A() b=A() print id(a),id(b)
結果:
34248016 34248016
IDは同じです。
この例では、Singleton メタクラスを構築し、 __call__ メソッドを使用して関数の動作をシミュレートします。クラス A を構築するときにそのメタクラスを Singleton に設定し、クラス オブジェクト A を作成するときに次のように動作します:
A=Singleton(name, baseds, class_dict), A は実際には Singleton クラスのインスタンスです。
A のインスタンスを作成するとき、A()=Singleton(name,bases,class_dict)()=Singleton(name,bases,class_dict).__call__() となり、A のすべてのインスタンスが A_ の属性を指すようになります。たとえば、この方法は実際には方法 1 と同じです。
方法 4
Python の module モジュールはプログラム内で 1 回だけロードされ、それ自体がシングルトンです。モジュールを直接記述し、モジュール内で必要なメソッドと属性をモジュール スコープ内の関数およびグローバル変数として記述することができます。クラスを記述する必要はまったくありません。
そして、モジュールとクラスの利点を組み合わせる方法がいくつかあります:
class _singleton(object): class ConstError(TypeError): pass def __setattr__(self,name,value): if name in self.__dict__: raise self.ConstError self.__dict__[name]=value def __delattr__(self,name): if name in self.__dict__: raise self.ConstError raise NameError import sys sys.modules[__name__]=_singleton()
Python は sys.modules をチェックしてモジュール オブジェクトであることを確認しません。これを使用してモジュールをクラス オブジェクトにバインドします。将来的には同じオブジェクトにバインドされます。
コードをsingle.pyに保存します:
>>> import single >>> single.a=1 >>> single.a=2
ConstError
>>> del single.a
ConstError
方法5
最も簡単な方法:
名前 singleton をインスタンスにバインドすると、singleton が独自のクラスの唯一のオブジェクトになります。