2

重複の可能性:
Python でシングルトンを定義するシンプルでエレガントな方法はありますか?

次のコード例では、Singleton からクラスを派生させています (それが 1 であることを願っています)。

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

class Tracer(Singleton):         
    def __init__(self):
        print "Init"

a = Tracer()
b = Tracer()

試してみると、 の__init__メソッドがTracer再度呼び出されていることがわかります。別のインスタンスに元のインスタンスを参照させるシングルトンがあるという意味ではありませんか? __init__以前の情報が上書きされる可能性があるため、メソッドを再度実行したくありません。シングルトンが間違っているか、使用されているのでしょうか。

4

2 に答える 2

6

以前の回答が機能しなかったため、削除しました。しかし、私はそうする高評価の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バージョン間のメタクラス構文の違いによるエラーが回避されます。(これは、定義されたメタクラスを明示的に使用して一時ベースクラスを作成することによって行われます。)

于 2012-12-09T23:33:02.997 に答える
2

あなた__init__は2回呼び出されますが、同じオブジェクト上にあります。シングルトンを作成しましたが、Pythonはそれを認識していないため、作成される各オブジェクトを初期化します。

シングルトンパターンを追求する場合は、初期化コードを__new__、または呼び出す別のメソッドに移動する必要があります__new__

覚えておいてください:

  1. シングルトンはJavaの標準ですが、Pythonでは嫌われています。

  2. シングルトンは、あるテストから次のテストに渡されるグローバルな状態であるため、コードのテストを困難にします。

于 2012-12-09T16:38:41.697 に答える