以前の回答が機能しなかったため、削除しました。しかし、私はそうする高評価のSOの答えを見つけました。主な違いは、ベースクラスの代わりにSingleton
メタクラス__call__()
を使用し、メソッドの代わりにインスタンスクラスのメソッドをオーバーロードすること__new__()
です。これにより、シングルトンクラスインスタンスのインスタンスの作成プロセスを制御する必要があります。これらの1つまたは複数を削除するための追加の方法を定義することは可能です—たとえばテスト目的のために。
もう1つの注目すべき実装の詳細は、メタクラスが_instances
単一の値しか保持できないものではなく、のディクショナリを維持することです。これにより、無数のシングルトンインスタンスを追跡できます(再利用可能であるため、複数のメタクラスである可能性があるため)。
これをサンプルコードに適用すると、次のようになります。
class Singleton(type):
"""Metaclass."""
_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 Tracer(object):
__metaclass__ = Singleton
def __init__(self):
print("Init")
a = Tracer()
b = Tracer()
print('a is b: {}'.format(a is b)) # same object? -> True
出力:
Init
a is b: True
アップデート
メタクラスを指定するための構文は、Python 2と3の間で異なります。後者の場合、Tracer
クラス定義を次のように変更する必要があります。
#!/usr/bin/env python3
class Tracer(object, metaclass=Singleton):
def __init__(self):
print("Init")
Pythonのバージョン2と3の両方で機能するものを書くことは可能ですが、次のように単純に条件付きで定義することはできないため、もう少し複雑です。
## Won't work ##
if sys.version_info[0] < 3: # Python 2?
class Tracer(object):
__metaclass__ = Singleton
def __init__(self):
print("Init")
else: # Python 3
class Tracer(object, metaclass=Singleton): # causes SyntaxError in Python 2
def __init__(self):
print("Init")
else
句の定義によりPython2でaが発生するためSyntaxError
です(ブロック内のコードが実際に実行されることはありませんが)。Benjamin Petersonの6つのモジュールのwith_metaclass()
関数が実行するのと同様の回避策であり、次のようになります。
class Tracer(Singleton("SingletonBaseClass", (object,), {})):
def __init__(self):
print("Init")
これにより、目的のメタクラスを継承するベースクラスが動的に作成されます。これにより、2つのPythonバージョン間のメタクラス構文の違いによるエラーが回避されます。(これは、定義されたメタクラスを明示的に使用して一時ベースクラスを作成することによって行われます。)