web-dev-qa-db-ja.com

Python:モジュールとその変数をシングルトンとして考える—クリーンなアプローチ?

Pythonプログラムにある種のシングルトンパターンを実装したいと思います。クラスを使用せずにそれを実行することを考えていました。つまり、すべてのシングルトン関連関数を配置したいと思います。そしてモジュール内の変数は、それを実際のシングルトンと見なします。

たとえば、これが「singleton_module.py」ファイルにあるとしましょう:

# singleton_module.py

# Singleton-related variables
foo = 'blah'
bar = 'stuff'

# Functions that process the above variables
def work(some_parameter):
    global foo, bar
    if some_parameter:
        bar = ...
    else:
        foo = ...

次に、プログラムの残りの部分(つまり、他のモジュール)はこのシングルトンを次のように使用します。

# another_module.py

import singleton_module

# process the singleton variables,
# which changes them across the entire program
singleton_module.work(...)

# freely access the singleton variables
# (at least for reading)
print singleton_module.foo

looksシングルトンを使用するモジュールではかなりクリーンであるため、これは私にはかなり良い考えのように思われました。

ただし、シングルトンモジュール内のこれらの退屈な「グローバル」ステートメントはすべて醜いです。それらは、シングルトン関連の変数を処理するすべての関数で発生します。この特定の例ではそれは多くありませんが、いくつかの関数で管理する10個以上の変数がある場合、それはきれいではありません。

また、グローバルステートメントを忘れた場合、これはかなりエラーが発生しやすくなります。関数にローカルな変数が作成され、モジュールの変数は変更されません。

それで、これはクリーンであると考えられますか? 「グローバル」な混乱をなんとかして取り除ける私のようなアプローチはありますか?

または、これは単に進むべき道ではないのですか?

33
user786233

モジュールをシングルトンとして使用する一般的な方法は、Alex Martelliの Borgパターン です。

class Borg:
    __shared_state = {}
    def __init__(self):
        self.__dict__ = self.__shared_state
    # and whatever else you want in your class -- that's all!

このクラスのインスタンスは複数存在する可能性がありますが、すべて同じ状態を共有しています。

22
Sven Marnach

多分あなたはすべての変数をグローバルなdictに置くことができ、あなたは "グローバル"なしで関数で直接dictを使うことができます。

# Singleton-related variables
my_globals = {'foo': 'blah', 'bar':'stuff'}

# Functions that process the above variables
def work(some_parameter):
    if some_parameter:
        my_globals['bar'] = ...
    else:
        my_globals['foo'] = ...

このようにできる理由は Python Scopes and Namespaces です。

4
WillYang

Pythonを使用してシングルトンパターンを実装する1つの方法は、次のようにすることもできます。

シングルトン__init()__メソッドを使用すると、クラスのインスタンスがすでに存在する場合に例外が発生します。より正確には、クラスにはメンバー__single_があります。このメンバーがNoneと異なる場合、例外が発生します。

_class Singleton:
    __single = None
    def __init__( self ):
        if Singleton.__single:
            raise Singleton.__single
        Singleton.__single = self  
_

シングルトンインスタンスの作成を例外付きで処理することも、あまりクリーンではないと主張できます。次のように、メソッドhandle()を使用して実装の詳細を非表示にすることができます

_def Handle( x = Singleton ):
    try:
        single = x()
    except Singleton, s:
        single = s
    return single 
_

このHandle()メソッドは、シングルトンパターンのC++実装と非常によく似ています。 Singletonクラスにhandle()を含めることができます

_Singleton& Singleton::Handle() {

  if( !psingle ) {
    psingle = new Singleton;
  }
  return *psingle;
}
_

新しいSingletonインスタンスまたはクラスSingletonの既存の一意のインスタンスへの参照を返します。

階層全体を処理する

_Single1_および_Single2_クラスがSingletonから派生した場合、派生クラスの1つを介したSingletonの単一インスタンスが存在します。これはこれで確認できます:

_>>> child = S2( 'singlething' )
>>> junior = Handle( S1)
>>> junior.name()
'singlething'
_
1
kiriloff

WillYangの答え を基に構築し、クリーンアップのためにさらに一歩進めます。グローバルディクショナリを保持するシンプルなクラスを定義して、参照しやすくします。

class struct(dict):
    def __init__(self, **kwargs):
        dict.__init__(self, kwargs)
        self.__dict__ = self

g = struct(var1=None, var2=None)

def func():
    g.var1 = dict()
    g.var3 = 10
    g["var4"] = [1, 2]
    print(g["var3"])
    print(g.var4)

以前と同じように、gに必要なものをすべて入れますが、今では非常にクリーンです。 :)

0
Adam S.

Svenの「ボーグパターン」の提案と同様に、クラスのインスタンスを作成せずに、すべての状態データをクラスに保持できます。この方法は新しいスタイルのクラスを利用していると思います。

このメソッドをBorgパターンに適用することもできます。ただし、クラスのインスタンスから状態メンバーを変更するには、インスタンスの__class__属性にアクセスする必要がある(ただし、instance.__class__.foo = 'z'ではなくinstance.foo = 'z'にアクセスする必要がありますが、stateclass.foo = 'z'を実行することもできます)。

class State:    # in some versions of Python, may need to be "class State():" or "class State(object):"
    __slots__ = []   # prevents additional attributes from being added to instances and same-named attributes from shadowing the class's attributes
    foo = 'x'
    bar = 'y'

    @classmethod
    def work(cls, spam):
        print(cls.foo, spam, cls.bar)

クラスの属性への変更は、インスタンス化後でもクラスのインスタンスに反映されることに注意してください。これには、新しい属性を追加したり、既存の属性を削除したりすることが含まれます。これにより、いくつかの興味深い、場合によっては有用な効果が得られます(場合によっては、実際に問題が発生する方法も確認できます)。自分で試してみてください。

0
JAB

正当なシングルトンの場合:

class SingletonMeta(type):
    __classes = {} # protect against defining class with the same name
    def __new__(cls, cls_name, cls_ancestors, cls_dict): 
         if cls_name in cls.__classes:
             return cls.__classes[cls_name]
         type_instance = super(SingletonMeta, cls).__new__(cls, cls_name, cls_ancestors, cls_dict) # pass 'type' instead of 'cls' if you dont want SingletonMeta's attributes reflected in the class
         return type_instance() # call __init__

class Singleton:
    __metaclass__ = SingletonMeta
    # define __init__ however you want

    __call__(self, *args, *kwargs):
        print 'hi!'

それが本当にシングルトンであることを確認するには、このクラスまたはそれから継承するクラスをインスタンス化してみてください。

singleton = Singleton() # prints "hi!"
0
Michael Ward