web-dev-qa-db-ja.com

Pythonでシングルトンを作成する

この質問は、 シングルトンデザインパターン が望ましいかどうか、反パターンか、あるいはあらゆる宗教戦争のための議論ではなく、このパターンがどのようにしてPythonに実装されるのかを議論するためのものですそれが最もPythonicな方法です。この場合、私は「ほとんどのピートニック」を「最小の驚きの原則」 に従うことを意味すると定義します。

シングルトンになるクラスが複数あります(私のユースケースはロガーのためのものですが、これは重要ではありません)。私が単純に継承または装飾することができるときに私は追加されたガムフでいくつかのクラスを雑然とさせたくありません。

最良の方法


方法1:デコレータ

def singleton(class_):
    instances = {}
    def getinstance(*args, **kwargs):
        if class_ not in instances:
            instances[class_] = class_(*args, **kwargs)
        return instances[class_]
    return getinstance

@singleton
class MyClass(BaseClass):
    pass

長所

  • デコレータは、多重継承よりも直感的な方法で加法的です。

短所

  • MyClass()を使用して作成されたオブジェクトは真のシングルトンオブジェクトですが、MyClass自体はクラスではなく関数であるため、そこからクラスメソッドを呼び出すことはできません。 m = MyClass(); n = MyClass(); o = type(n)();そしてm == n && m != o && n != oについても

方法2:基本クラス

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

class MyClass(Singleton, BaseClass):
    pass

長所

  • それは本当のクラスです

短所

  • 多重継承 - えー! __new__は、2番目の基本クラスからの継承中に上書きされる可能性がありますか?必要以上のことを考えなければなりません。

方法3:A メタクラス

class Singleton(type):
    _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]

#Python2
class MyClass(BaseClass):
    __metaclass__ = Singleton

#Python3
class MyClass(BaseClass, metaclass=Singleton):
    pass

長所

  • それは本当のクラスです
  • 継承を自動魔法でカバー
  • __metaclass__をその適切な目的のために使用します(そしてそれを私に知らせました)

短所

  • いずれかがあります?

方法4:デコレータが同じ名前のクラスを返す

def singleton(class_):
    class class_w(class_):
        _instance = None
        def __new__(class_, *args, **kwargs):
            if class_w._instance is None:
                class_w._instance = super(class_w,
                                    class_).__new__(class_,
                                                    *args,
                                                    **kwargs)
                class_w._instance._sealed = False
            return class_w._instance
        def __init__(self, *args, **kwargs):
            if self._sealed:
                return
            super(class_w, self).__init__(*args, **kwargs)
            self._sealed = True
    class_w.__= class_.__name__
    return class_w

@singleton
class MyClass(BaseClass):
    pass

長所

  • それは本当のクラスです
  • 継承を自動魔法でカバー

短所

  • それぞれの新しいクラスを作成するためのオーバーヘッドはありませんか?ここではシングルトンを作りたいクラスごとに2つのクラスを作成しています。私の場合はこれで問題ありませんが、これではうまくいかないことがあります。もちろん、このパターンを拡大縮小するのがあまりにも簡単であることが原因であるかどうかに関しては議論の余地があります。
  • _sealed属性のポイントは何ですか
  • super()を使用して基本クラスで同じ名前のメソッドを呼び出すことはできません。つまり、__new__をカスタマイズできず、__init__を呼び出す必要があるクラスをサブクラス化することはできません。
740
theheadofabroom
class Foo(object):
     pass

some_global_variable = Foo()

モジュールは一度だけインポートされ、他のものはすべてひっくり返ります。シングルトンを使用せず、グローバルを使用しないようにしてください。

75
Cat Plus Plus

モジュールを使用してください。一度だけインポートされます。その中にいくつかのグローバル変数を定義します - それらはシングルトンの '属性'になります。いくつかの機能を追加します - シングルトンの「メソッド」です。

56
warvariuc

あなたはおそらくPythonのシングルトンを必要としないでしょう。すべてのデータと関数をモジュールに定義するだけで、事実上のシングルトンができます。

あなたが本当に絶対にシングルトンクラスを持っている必要があるならば、私は一緒に行きます:

class My_Singleton(object):
    def foo(self):
        pass

my_singleton = My_Singleton()

使用するには:

from mysingleton import my_singleton
my_singleton.foo()

mysingleton.pyはMy_Singletonが定義されているファイル名です。これは、ファイルが初めてインポートされた後、Pythonがコードを再実行しないために機能します。

21
Alan Dyke

これがあなたのためのワンライナーです:

singleton = lambda c: c()

使い方は次のとおりです。

@singleton
class wat(object):
    def __init__(self): self.x = 1
    def get_x(self): return self.x

assert wat.get_x() == 1

あなたのオブジェクトは熱心にインスタンス化されます。これはあなたが望むものかもしれません。

14
Jonas Kölker

Stack Overflowに関する質問をチェックしてください。Pythonでシングルトンを定義するための簡単で洗練された方法はありますか。いくつかの解決策があります。

私は、Alex Martelliによるpythonのデザインパターンに関する講演を見ることを強くお勧めします: part 1 および part 2 。特に、第1回で、彼はシングルトン/共有状態オブジェクトについて話しました。

7
Anton

これが私自身のシングルトンの実装です。あなたがしなければならないのはクラスを飾ることだけです。シングルトンを取得するには、Instanceメソッドを使用する必要があります。これが例です:

   @Singleton
   class Foo:
       def __init__(self):
           print 'Foo created'

   f = Foo() # Error, this isn't how you get the instance of a singleton

   f = Foo.Instance() # Good. Being explicit is in line with the Python Zen
   g = Foo.Instance() # Returns already created instance

   print f is g # True

そしてこれがそのコードです。

class Singleton:
    """
    A non-thread-safe helper class to ease implementing singletons.
    This should be used as a decorator -- not a metaclass -- to the
    class that should be a singleton.

    The decorated class can define one `__init__` function that
    takes only the `self` argument. Other than that, there are
    no restrictions that apply to the decorated class.

    To get the singleton instance, use the `Instance` method. Trying
    to use `__call__` will result in a `TypeError` being raised.

    Limitations: The decorated class cannot be inherited from.

    """

    def __init__(self, decorated):
        self._decorated = decorated

    def Instance(self):
        """
        Returns the singleton instance. Upon its first call, it creates a
        new instance of the decorated class and calls its `__init__` method.
        On all subsequent calls, the already created instance is returned.

        """
        try:
            return self._instance
        except AttributeError:
            self._instance = self._decorated()
            return self._instance

    def __call__(self):
        raise TypeError('Singletons must be accessed through `Instance()`.')

    def __instancecheck__(self, inst):
        return isinstance(inst, self._decorated)
3
Paul Manta

方法3はとてもすっきりしているように見えますが、あなたのプログラムを Python 2Python 3 の両方で実行したいのであれば、うまくいきません。 Python 3バージョンではPython 2の構文エラーが発生するため、Pythonバージョンのテストで個別のバリアントを保護することもできません。

Mike Watkinsに感謝: http://mikewatkins.ca/2008/11/29/python-2-and-3-metaclasses/ 。プログラムをPython 2とPython 3の両方で動作させたい場合は、次のようにする必要があります。

class Singleton(type):
    _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]

MC = Singleton('MC', (object), {})

class MyClass(MC):
    pass    # Code for the class implementation

代入の 'object'は 'BaseClass'に置き換える必要があると思いますが、試したことはありません(図のようにコードを試しました)。

2
Tim

これはどう:

def singleton(cls):
    instance=cls()
    cls.__new__ = cls.__call__= lambda cls: instance
    cls.__init__ = lambda self: None
    return instance

シングルトンであるべきクラスのデコレータとしてそれを使用してください。このような:

@singleton
class MySingleton:
    #....

これは別の答えではsingleton = lambda c: c()デコレータに似ています。他の解決策と同様に、唯一のインスタンスはクラスの名前(MySingleton)を持ちます。ただし、この解決策では、MySingleton()を実行することで、クラスからインスタンスを「作成」(実際には唯一のインスタンスを取得)できます。また、type(MySingleton)()(同じインスタンスを返す)を実行して追加のインスタンスを作成することもできません。

1
Tolli

前の答えは正しいですが、私は方法1の質問の一部として投稿された「MyClass自体はクラスではなく関数であるため、クラスメソッドを呼び出すことはできません」というステートメントに同意しません。下記の私の例を見てください。そのメソッドは、singletonタグで装飾されたMyClassで複数回呼び出されています。

また、これは投稿されたいくつかの回答と非常によく似ており、 python document に基づいていますが、クラスと関数は1または0の引数を受け取り、シングルトンでも動作するように設計されているため、少し異なります。

enter image description here これは、シングルトンのメソッドを複数回呼び出すことができ、クラスの1つのインスタンスがまだ使用されており、新しいオブジェクトが作成されていないことを示しています。

#/usr/bin/env python

def singleton(cls):
    instances = {}
    def getinstance(anyArgs=None):
        if cls not in instances:
            instances[cls] = cls(anyArgs)
        return instances[cls]
    return getinstance

@singleton
class MyClass:
    def __init__(self,count=None):
        print("print argument if any exists",count)

    def test(self, counter):
        # time.sleep(1000)
        print("-->",counter)
        return counter

### create two objects to see if we get a singleton behavior !
a = MyClass(10000)
a.test(1)
b = MyClass()
b.test(2)
if a != b:
    print("this is not a singleton")

#lets makesure it's still is the same object
if a!=b:
    print("error")

Theheadofabroom(質問を投稿した人)が彼の最初の質問についていくつかのフィードバックを与えてから、私は彼のフィードバックに基づいて新しい解決策に取り組みました(私は以前の答えを続けました。 heheadofabroomが求めているのは100%正確ではありません。これが更新された答えです。

enter image description here

コピーして貼り付けるためのコードは次のとおりです。

#/usr/bin/env python
from functools import wraps 

def singleton(cls):
    instances = {}
    def getinstance(anyArgs=None):
        if cls not in instances:
            instances[cls] = cls(anyArgs)
            return instances[cls]
    return getinstance

def add_function(cls):
    def outer_decorate_it(somefunction):
        @wraps(somefunction) 
        def wrapper( *args, **kwargs): 
            return somefunction(*args, **kwargs)
        setattr(cls, somefunction.__name__, wrapper)
        return somefunction
    return outer_decorate_it

@singleton
class MyClass():
    def __init__(self,count=None):
        print("print argument if any exists",count)

@add_function(MyClass)
def testit():
    print("It's me the function from the class")


MyClass.testit()
1
grepit

さて、モジュールレベルのグローバルを持つことについての一般的なPythonicの提案に同意する以外に、これはどうですか?

def singleton(class_):
    class class_w(class_):
        _instance = None
        def __new__(class2, *args, **kwargs):
            if class_w._instance is None:
                class_w._instance = super(class_w, class2).__new__(class2, *args, **kwargs)
                class_w._instance._sealed = False
            return class_w._instance
        def __init__(self, *args, **kwargs):
            if self._sealed:
                return
            super(class_w, self).__init__(*args, **kwargs)
            self._sealed = True
    class_w.__= class_.__name__
    return class_w

@singleton
class MyClass(object):
    def __init__(self, text):
        print text
    @classmethod
    def name(class_):
        print class_.__name__

x = MyClass(111)
x.name()
y = MyClass(222)
print id(x) == id(y)

出力は以下のとおりです。

111     # the __init__ is called only on the 1st time
MyClass # the __is preserved
True    # this is actually the same instance
1
Guard

Tolli's answer に基づくコード。

#decorator, modyfies new_cls
def _singleton(new_cls):
    instance = new_cls()                                              #2
    def new(cls):
        if isinstance(instance, cls):                                 #4
            return instance
        else:
            raise TypeError("I can only return instance of {}, caller wanted {}".format(new_cls, cls))
    new_cls.__new__  = new                                            #3
    new_cls.__init__ = lambda self: None                              #5
    return new_cls


#decorator, creates new class
def singleton(cls):
    new_cls = type('singleton({})'.format(cls.__name__), (cls,), {} ) #1
    return _singleton(new_cls)


#metaclass
def meta_singleton(name, bases, attrs):
    new_cls = type(name, bases, attrs)                                #1
    return _singleton(new_cls)

説明:

  1. 与えられたclsから継承して、新しいクラスを作成します
    (誰かがsingleton(list)などを望んでいる場合には、clsは変更されません)

  2. インスタンスを作成します。 __new__をオーバーライドする前は、とても簡単です。

  3. さて、インスタンスを簡単に作成したら、先ほど定義したメソッドを使って__new__をオーバーライドします。
  4. この関数は、呼び出し元が期待しているものだけがinstanceを返し、そうでなければTypeErrorを送出します。
    誰かが装飾されたクラスから継承しようとしたときに条件が満たされません。

  5. __new__()clsのインスタンスを返す場合、新しいインスタンスの__init__()メソッドが呼び出されます _は、__init__(self[, ...])のように、selfは新しいインスタンスで、残りの引数は__new__()に渡されたものと同じです。

    instanceはすでに初期化されているので、functionは__init__を何もしないfunctionに置き換えます。

それがオンラインで働いているのを見る

0
GingerPlusPlus

それはfabによる答えに少し似ていますが、まったく同じではありません。

シングルトン規約 は、コンストラクタを複数回呼び出すことができることを要求しません。シングルトンは一度だけ作成されるべきなので、それは一度だけ作成されるように見られるべきではありませんか?コンストラクタを「なりすまし」すると、読みやすさが損なわれることは間違いありません。

だから私の提案はちょうどこれです:

class Elvis():
    def __init__(self):
        if hasattr(self.__class__, 'instance'):
            raise Exception()
        self.__class__.instance = self
        # initialisation code...

    @staticmethod
    def the():
        if hasattr(Elvis, 'instance'):
            return Elvis.instance
        return Elvis()

これは、ユーザーコードによるコンストラクターやフィールドinstanceの使用を除外するものではありません。

if Elvis() is King.instance:

... Elvisがまだ作成されていないこと、そしてKingが作成されていることを確実に知っていれば。

しかしusersがtheメソッドを普遍的に使うことを推奨します。

Elvis.the().leave(Building.the())

これを完全にするために、instanceを削除しようとした場合は__delattr__()をオーバーライドしてExceptionを発生させ、__del__()をオーバーライドしてExceptionを発生させることもできます(プログラムが終了していることがわからない限り)。

さらなる改良


コメントや編集を手伝ってくれた方々、どうもありがとうございました。私はJythonを使用していますが、これはより一般的に機能し、スレッドセーフであるはずです。

try:
    # This is jython-specific
    from synchronize import make_synchronized
except ImportError:
    # This should work across different python implementations
    def make_synchronized(func):
        import threading
        func.__lock__ = threading.Lock()

        def synced_func(*args, **kws):
            with func.__lock__:
                return func(*args, **kws)

        return synced_func

class Elvis(object): # NB must be subclass of object to use __new__
    instance = None

    @classmethod
    @make_synchronized
    def __new__(cls, *args, **kwargs):
        if cls.instance is not None:
            raise Exception()
        cls.instance = object.__new__(cls, *args, **kwargs)
        return cls.instance

    def __init__(self):
        pass
        # initialisation code...

    @classmethod
    @make_synchronized
    def the(cls):
        if cls.instance is not None:
            return cls.instance
        return cls()

注意点:

  1. あなたがpython2.xのオブジェクトからサブクラス化しないならば、あなたは__new__を使わない古いスタイルのクラスを得るでしょう
  2. __new__を装飾するときは@classmethodで装飾する必要があります。そうしないと__new__がアンバインドされたインスタンスメソッドになります。
  3. これはtheをクラスレベルのプロパティにすることを可能にし、おそらくそれをinstanceにリネームすることを可能にするので、これはおそらくメタクラスの使用によって改善することができます。
0
mike rodent

一つのライナー(私は誇りに思っていませんが、それは仕事をします):

class Myclass:
  def __init__(self):
      # do your stuff
      globals()[type(self).__name__] = lambda: self # singletonify
0
polvoazul

Singletonのインスタンスを遅延初期化する必要がない場合は、次の手順が簡単でスレッドセーフです。

class A:
    instance = None
    # Methods and variables of the class/object A follow
A.instance = A()

このようにAはモジュールのインポート時に初期化されるシングルトンです。

0
Serge Rogatch

私は私のリングに投げます。シンプルなデコレーターです。

from abc import ABC

def singleton(real_cls):

    class SingletonFactory(ABC):

        instance = None

        def __new__(cls, *args, **kwargs):
            if not cls.instance:
                cls.instance = real_cls(*args, **kwargs)
            return cls.instance

    SingletonFactory.register(real_cls)
    return SingletonFactory

# Usage
@singleton
class YourClass:
    ...  # Your normal implementation, no special requirements.

利点私はそれが他の解決策のいくつかに対して持っていると思います:

  • それは明確かつ簡潔です(私の目には、D)。
  • その作用は完全にカプセル化されています。 YourClassの実装について一つだけ変更する必要はありません。これには、あなたのクラスにメタクラスを使用する必要がないことも含まれます(上記のメタクラスはファクトリ上にあり、「実際の」クラスにはありません)。
  • それは猿のパッチを当てることに頼っていません。
  • 発信者には透過的です:
    • 呼び出し元はまだYourClassを単純にインポートします。それはクラスのように見え(それがそうであるため)、そして彼らは通常それを使います。発信者をファクトリ機能に適応させる必要はありません。
    • YourClass()がインスタンス化するものは、まだあなたが実装したYourClassの本当のインスタンスであり、いかなる種類のプロキシでもないので、その結果生じる副作用の可能性はありません。
    • isinstance(instance, YourClass)と同様の操作はまだ期待通りに動作します(ただしこのビットにはabcが必要なのでPython <2.6は使えません)。

1つ欠点があります。実際のクラスのクラスメソッドと静的メソッドは、それを隠しているファクトリクラスを介して透過的に呼び出すことはできません。このようなニーズに遭遇することは決してないので、これを使用することはめったにありませんでしたが、__getattr__()を実装するファクトリ上のカスタムメタクラスを使用して本当のクラスにall-ish属性アクセスを委任することで簡単に修正できます。

私が実際にもっと便利だと思った(この種のことがとても頻繁に必要とされているわけではない)関連パターンは、 同じ引数でクラスをインスタンス化する という結果になる "Unique"パターンです。同じインスタンスを取り戻します。すなわち「引数ごとのシングルトン」上記はこれによく適応し、さらに簡潔になります。

def unique(real_cls):

    class UniqueFactory(ABC):

        @functools.lru_cache(None)  # Handy for 3.2+, but use any memoization decorator you like
        def __new__(cls, *args, **kwargs):
            return real_cls(*args, **kwargs)

    UniqueFactory.register(real_cls)
    return UniqueFactory

とは言っても、私はあなたがこれらのことのうちの1つが必要であると思うなら、あなたは本当にしばらくの間停止して、あなたが本当にそうであるかどうかあなた自身に尋ねるべきだという一般的なアドバイスに同意します。 99%の時間、YAGNI。

0
CryingCyclops

たぶん私はシングルトンパターンを理解し損ねるかもしれませんが、私の解決策はこのシンプルで実用的です(pythonic?)。このコードは2つの目標を満たします

  1. Fooのインスタンスをどこからでもアクセス可能にします(グローバル)。
  2. Fooのインスタンスは1つだけ存在できます。

これがコードです。

#!/usr/bin/env python3

class Foo:
    me = None

    def __init__(self):
        if Foo.me != None:
            raise Exception('Instance of Foo still exists!')

        Foo.me = self


if __== '__main__':
    Foo()
    Foo()

出力

Traceback (most recent call last):
  File "./x.py", line 15, in <module>
    Foo()
  File "./x.py", line 8, in __init__
    raise Exception('Instance of Foo still exists!')
Exception: Instance of Foo still exists!
0
buhtz
  • 同じクラスの複数のインスタンスを持ちたいが、引数またはkwargsが異なる場合にのみ、 this を使用できます。
    1. serial通信を処理するクラスがあり、引数としてシリアルポートを送信するインスタンスを作成する場合、従来のアプローチでは機能しません
    2. 上記のデコレータを使用すると、引数が異なる場合、クラスの複数のインスタンスを作成できます。
    3. 同じ引数の場合、デコレータはすでに作成されている同じインスタンスを返します。
>>> from decorators import singleton
>>>
>>> @singleton
... class A:
...     def __init__(self, *args, **kwargs):
...         pass
...
>>>
>>> a = A(name='Siddhesh')
>>> b = A(name='Siddhesh', lname='Sathe')
>>> c = A(name='Siddhesh', lname='Sathe')
>>> a is b  # has to be different
False
>>> b is c  # has to be same
True
>>>