Python でシングルトン モードを実装する 5 つの方法

WBOY
リリース: 2023-05-26 22:31:24
転載
1527 人が閲覧しました

Python 实现单例模式的五种写法

シングルトン パターンは、一般的に使用されるソフトウェア デザイン パターンであり、このパターンの主な目的は、特定のクラスのインスタンスが 1 つだけ存在するようにすることです。シングルトン オブジェクトは、システム全体で特定のクラスのインスタンスを 1 つだけ表示したい場合に便利です。

たとえば、サーバー プログラムの構成情報はファイルに保存され、クライアントは AppConfig クラスを通じて構成ファイルの情報を読み取ります。プログラムの実行中に構成ファイルの内容をさまざまな場所で使用する必要がある場合、つまり、AppConfig オブジェクトのインスタンスをさまざまな場所で作成する必要がある場合、複数の AppConfig インスタンス オブジェクトが存在することになります。特に構成ファイルに多くのコンテンツが含まれている場合、メモリ リソースが大幅に浪費されます。

実際、AppConfig のようなクラスの場合、プログラムの実行中にインスタンス オブジェクトが 1 つだけ存在することが望まれます。

Python では、さまざまな方法を使用してシングルトン パターンを実装できます。

  1. モジュールの使用
  2. デコレータの使用
  3. クラスの使用
  4. __new__ メソッドに基づいて実装
  5. メタクラス メソッドに基づいて実装

以下詳細な紹介:

モジュールの使用

実際、Python モジュールは自然なシングルトン モードです。これは、モジュールが初めてインポートされるときに、.pyc ファイルが2 回目にインポートすると、モジュール コードを再度実行することなく、.pyc ファイルが直接ロードされます。

したがって、シングルトン オブジェクトを取得するには、モジュール内で関連する関数とデータを定義するだけで済みます。

本当にシングルトン クラスが必要な場合は、これを行うことを検討できます:

class Singleton(object):
 def foo(self):
 pass
singleton = Singleton()
ログイン後にコピー

上記のコードをファイル mysingleton.py に保存します。このファイルのオブジェクトを他のファイルにインポートします。このオブジェクトはシングルトン モードのオブジェクトです。

from mysingleton import singleton
ログイン後にコピー

デコレータを使用します

def Singleton(cls):
 _instance = {}
 def _singleton(*args, **kargs):
 if cls not in _instance:
 _instance[cls] = cls(*args, **kargs)
 return _instance[cls]
 return _singleton
@Singleton
class A(object):
 a = 1
 def __init__(self, x=0):
 self.x = x
a1 = A(2)
a2 = A(3)
ログイン後にコピー

クラスを使用します

class Singleton(object):
 def __init__(self):
 pass
 @classmethod
 def instance(cls, *args, **kwargs):
 if not hasattr(Singleton, "_instance"):
 Singleton._instance = Singleton(*args, **kwargs)
 return Singleton._instance
ログイン後にコピー

一般に、これでシングルトン モードが完了すると誰もが考えますが、マルチスレッドを使用する場合には問題が発生します。

class Singleton(object):
 def __init__(self):
 pass
 @classmethod
 def instance(cls, *args, **kwargs):
 if not hasattr(Singleton, "_instance"):
 Singleton._instance = Singleton(*args, **kwargs)
 return Singleton._instance
import threading
def task(arg):
 obj = Singleton.instance()
 print(obj)
for i in range(10):
 t = threading.Thread(target=task,args=[i,])
 t.start()
ログイン後にコピー

プログラムを実行すると、出力結果は次のようになります。

<__main__.Singleton object at 0x02C933D0>
<__main__.Singleton object at 0x02C933D0>
<__main__.Singleton object at 0x02C933D0>
<__main__.Singleton object at 0x02C933D0>
<__main__.Singleton object at 0x02C933D0>
<__main__.Singleton object at 0x02C933D0>
<__main__.Singleton object at 0x02C933D0>
<__main__.Singleton object at 0x02C933D0>
<__main__.Singleton object at 0x02C933D0>
<__main__.Singleton object at 0x02C933D0>
ログイン後にコピー

参照 問題はありませんが、実行速度が速すぎるため、__init__ メソッド内に IO 処理が含まれている場合に問題が発生します。

以下では、time.sleep を通じてシミュレーションを行います。上記の __init__ メソッドに次のコードを追加します:

def __init__(self):
 import time
 time.sleep(1)
ログイン後にコピー

プログラムを再実行すると、結果は次のようになります:

<__main__.Singleton object at 0x034A3410>
<__main__.Singleton object at 0x034BB990>
<__main__.Singleton object at 0x034BB910>
<__main__.Singleton object at 0x034ADED0>
<__main__.Singleton object at 0x034E6BD0>
<__main__.Singleton object at 0x034E6C10>
<__main__.Singleton object at 0x034E6B90>
<__main__.Singleton object at 0x034BBA30>
<__main__.Singleton object at 0x034F6B90>
<__main__.Singleton object at 0x034E6A90>
ログイン後にコピー

問題が発生しました。上記の方法で作成されたシングルトンはマルチスレッドをサポートできません。

#解決策: ロックしてください!ロック解除された部分は同時に実行され、ロックされた部分はシリアルに実行されるため、速度は低下しますが、データのセキュリティは確保されます。

import time
import threading
class Singleton(object):
 _instance_lock = threading.Lock()
 def __init__(self):
 time.sleep(1)
 @classmethod
 def instance(cls, *args, **kwargs):
 with Singleton._instance_lock:
 if not hasattr(Singleton, "_instance"):
 Singleton._instance = Singleton(*args, **kwargs)
 return Singleton._instance
def task(arg):
 obj = Singleton.instance()
 print(obj)
for i in range(10):
 t = threading.Thread(target=task,args=[i,])
 t.start()
time.sleep(20)
obj = Singleton.instance()
print(obj)
ログイン後にコピー

出力結果は次のとおりです。

<__main__.Singleton object at 0x02D6B110>
<__main__.Singleton object at 0x02D6B110>
<__main__.Singleton object at 0x02D6B110>
<__main__.Singleton object at 0x02D6B110>
<__main__.Singleton object at 0x02D6B110>
<__main__.Singleton object at 0x02D6B110>
<__main__.Singleton object at 0x02D6B110>
<__main__.Singleton object at 0x02D6B110>
<__main__.Singleton object at 0x02D6B110>
<__main__.Singleton object at 0x02D6B110>
ログイン後にコピー

これはほぼ同じですが、小さな問題がまだあります。つまり、time.sleep(20 の後のプログラムの実行時) です。 ) が実行されると、オブジェクトは以下でインスタンス化され、すでにシングルトン モードになっています。


しかし、まだロックが追加されていますが、これは良くありません。いくつかの最適化を行い、インスタンス メソッドを次のように変更します:

@classmethod
def instance(cls, *args, **kwargs):
 if not hasattr(Singleton, "_instance"):
 with Singleton._instance_lock:
 if not hasattr(Singleton, "_instance"):
 Singleton._instance = Singleton(*args, **kwargs)
 return Singleton._instance
ログイン後にコピー

このようにして、複数のロックをサポートできます。スレッド シングルトンモードが完了します。

import time
import threading
class Singleton(object):
 _instance_lock = threading.Lock()
 def __init__(self):
 time.sleep(1)
 @classmethod
 def instance(cls, *args, **kwargs):
 if not hasattr(Singleton, "_instance"):
 with Singleton._instance_lock:
 if not hasattr(Singleton, "_instance"):
 Singleton._instance = Singleton(*args, **kwargs)
 return Singleton._instance
def task(arg):
 obj = Singleton.instance()
 print(obj)
for i in range(10):
 t = threading.Thread(target=task,args=[i,])
 t.start()
time.sleep(20)
obj = Singleton.instance()
print(obj)
ログイン後にコピー

この方法で実装されたシングルトン モードには使用上の制限があります。後のインスタンス化は obj = Singleton.instance() を介して行う必要があります。


obj = Singleton() が使用される場合、この方法で得られるものはシングルトンではありません。


__new__ メソッドに基づく


上記の例を通じて、シングルトンを実装するときに、スレッドの安全性を確保するために内部的にロックを追加する必要があることがわかります。


オブジェクトをインスタンス化するときは、まずクラスの __new__ メソッドを実行し (メソッドを記述しない場合は、デフォルトで object.__new__ が呼び出されます)、オブジェクトをインスタンス化します。次にクラスを実行します。 __init__ メソッドはこのオブジェクトを初期化するため、これに基づいてシングルトン モードを実装できます。

import threading
class Singleton(object):
 _instance_lock = threading.Lock()
 def __init__(self):
 pass
 def __new__(cls, *args, **kwargs):
 if not hasattr(Singleton, "_instance"):
 with Singleton._instance_lock:
 if not hasattr(Singleton, "_instance"):
 Singleton._instance = object.__new__(cls)
 return Singleton._instance
obj1 = Singleton()
obj2 = Singleton()
print(obj1,obj2)
def task(arg):
 obj = Singleton()
 print(obj)
for i in range(10):
 t = threading.Thread(target=task,args=[i,])
 t.start()
ログイン後にコピー

出力結果は次のとおりです。

<__main__.Singleton object at 0x038B33D0> <__main__.Singleton object at 0x038B33D0>
<__main__.Singleton object at 0x038B33D0>
<__main__.Singleton object at 0x038B33D0>
<__main__.Singleton object at 0x038B33D0>
<__main__.Singleton object at 0x038B33D0>
<__main__.Singleton object at 0x038B33D0>
<__main__.Singleton object at 0x038B33D0>
<__main__.Singleton object at 0x038B33D0>
<__main__.Singleton object at 0x038B33D0>
<__main__.Singleton object at 0x038B33D0>
<__main__.Singleton object at 0x038B33D0>
ログイン後にコピー

このシングルトン モードを使用すると、今後オブジェクトをインスタンス化するとき、オブジェクトのインスタンス化方法は通常の obj = Singleton() と同じになります。 。


メタクラス メソッドに基づいて実装


関連知識:


    クラスは型ごとに作成されます。クラスを作成するときは、型の__init__ メソッドは自動的に実行され、class() はタイプの __call__ メソッド (クラスの __new__ メソッド、クラスの __init__ メソッド) を実行します。
  1. オブジェクトはクラスによって作成されます。オブジェクトを作成すると、クラスの __init__ メソッドが自動的に実行され、object() によってクラスの __call__ メソッドが実行されます。
  2. 例子:
    class Foo:
     def __init__(self):
     pass
     def __call__(self, *args, **kwargs):
     pass
    obj = Foo()
    # 执行type的 __call__ 方法,调用 Foo类(是type的对象)的 __new__方法,用于创建对象,然后调用 Foo类(是type的对象)的 __init__方法,用于对对象初始化。
    obj()# 执行Foo的 __call__ 方法
    ログイン後にコピー
メタクラスの使用:

class SingletonType(type):
 def __init__(self,*args,**kwargs):
 super(SingletonType,self).__init__(*args,**kwargs)
 def __call__(cls, *args, **kwargs): # 这里的cls,即Foo类
 print('cls',cls)
 obj = cls.__new__(cls,*args, **kwargs)
 cls.__init__(obj,*args, **kwargs) # Foo.__init__(obj)
 return obj
class Foo(metaclass=SingletonType): # 指定创建Foo的type为SingletonType
 def __init__(self,name):
 self.name = name
 def __new__(cls, *args, **kwargs):
 return object.__new__(cls)
obj = Foo('xx')
ログイン後にコピー

シングルトン モードの実装:


import threading
class SingletonType(type):
 _instance_lock = threading.Lock()
 def __call__(cls, *args, **kwargs):
 if not hasattr(cls, "_instance"):
 with SingletonType._instance_lock:
 if not hasattr(cls, "_instance"):
 cls._instance = super(SingletonType,cls).__call__(*args, **kwargs)
 return cls._instance
class Foo(metaclass=SingletonType):
 def __init__(self,name):
 self.name = name
obj1 = Foo('name')
obj2 = Foo('name')
print(obj1,obj2)
ログイン後にコピー

以上がPython でシングルトン モードを実装する 5 つの方法の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

ソース:51cto.com
このウェブサイトの声明
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。
最新の問題
人気のチュートリアル
詳細>
最新のダウンロード
詳細>
ウェブエフェクト
公式サイト
サイト素材
フロントエンドテンプレート