[解決済み] Pythonでシングルトンを作成する
質問
の可否を議論するための質問ではありません。 シングルトンデザインパターン しかし、このパターンをPythonで実装する場合、どのように実装するのが最もPythonicであるかを議論したいのです。この例では、私は「最も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
Pros
- デコレータは、多重継承よりも直感的な方法で追加することができます。
短所
-
を使用して作成されたオブジェクトは
MyClass()
は真のシングルトン・オブジェクトになる。MyClass
自体はクラスではなく関数なので、そこからクラスメソッドを呼び出すことはできません。またx = MyClass(); y = MyClass(); t = type(n)();
では
x == y
しかし
x != t && y != t
方法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
Pros
- 真のクラスである
短所
-
多重継承 - eugh!
__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
Pros
- 真のクラスである
- オートマティックに継承をカバーする
-
使用方法
__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.__name__ = class_.__name__
return class_w
@singleton
class MyClass(BaseClass):
pass
Pros
- 真のクラスである
- オートマティックに継承をカバーする
短所
- 新しいクラスを作成するたびにオーバーヘッドが発生しないか?ここでは、シングルトンにしたいクラスごとに2つのクラスを作成しています。私の場合はこれで問題ないのですが、これがスケールしないのではないかと心配になります。もちろん、このパターンをスケールさせるのが簡単すぎるかどうかという議論もありますが...。
-
のポイントは何でしょうか?
_sealed
属性 -
を使用して、ベース・クラスの同名のメソッドを呼び出すことはできません。
super()
というのも、再帰的に検索されるからです。つまり__new__
まで呼び出す必要があるようなクラスのサブクラスはできません。__init__
.
方法5:モジュール
モジュールファイル
singleton.py
Pros
- 複雑なものよりシンプルなものが良い
短所
- 遅延インスタンス化されない
解決方法は?
メタクラスを使用する
私がお勧めするのは 方法その2 を使用したほうがよいでしょう。 メタクラス ベースクラスより 以下は実装例です。
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]
class Logger(object):
__metaclass__ = Singleton
または、Python3 の場合
class Logger(metaclass=Singleton):
pass
を実行したい場合
__init__
が呼び出されるたびに
else:
cls._instances[cls].__init__(*args, **kwargs)
から
if
の文は
Singleton.__call__
.
メタクラスについて少し説明します。メタクラスとは
クラスの
つまり、クラスは
そのメタクラスのインスタンス
. Pythonでオブジェクトのメタクラスを見つけるには、次のようにします。
type(obj)
. 通常の新スタイルのクラスは
type
.
Logger
は、上記のコードでは
class 'your_module.Singleton'
の(唯一の)インスタンスと同じように
Logger
の型になります。
class 'your_module.Logger'
. でロガーを呼び出すと
Logger()
のメタクラスを問い合わせます。
Logger
,
Singleton
を実行することで、インスタンスの生成を先取りすることができます。この処理は、Pythonがクラスに何をすべきか尋ねるために
__getattr__
でその属性を参照したとき、その属性は
myclass.attribute
.
メタクラスは、基本的に
クラスの定義が意味するもの
とその定義をどのように実装するかということです。例えば
http://code.activestate.com/recipes/498149/
これは、本質的にC言語スタイルの
struct
をメタクラスを使ってPythonで表現しています。スレッド
メタクラスの(具体的な)使用例にはどのようなものがありますか?
一般的には、宣言的なプログラミング、特にORMで使われるようなものに関連しているようです。
このような状況で、もしあなたが自分の
メソッド#2
を定義し、サブクラスが
__new__
メソッドを使用する場合、それは
毎回実行される
を呼び出すと
SubClassOfSingleton()
-- これは、ストアドインスタンスを返すメソッドを呼び出す役割を担っているからです。メタクラスでは
一度だけ呼ばれる
唯一のインスタンスが作成されたときです。あなたが望むのは
クラスを呼び出す意味をカスタマイズする
これは型によって決定されます。
一般的には 意味をなす メタクラスを使ってシングルトンを実装することです。シングルトンは特別なもので、それは 一度だけ作成される をカスタマイズするのがメタクラスです。 クラスの作成 . メタクラスを使用すると、次のようなことが可能になります。 より多くの制御 というのは、他の方法でシングルトン・クラスの定義をカスタマイズする必要がある場合です。
あなたのシングルトン
は多重継承を必要としません。
(メタクラスは基底クラスではないので)しかし
作成されたクラスのサブクラス
多重継承を使用する場合は、シングルトン・クラスが
最初 / 最左
を再定義するメタクラスを持つものである。
__call__
これは問題になる可能性は非常に低いです。インスタンスディクトは
インスタンスの名前空間でない
ので、誤って上書きすることはないでしょう。
また、シングルトンパターンは単一責任原則(Single Responsibility Principle)に反しているという話も聞きます。 たった一つのこと . そうすれば、別のものを変更する必要がある場合でも、コードが台無しになることを心配する必要がなくなります。メタクラスの実装 このテストに合格 . このメタクラスは パターンを実行する である必要はなく、作成されたクラスとサブクラスは シングルトンであることを意識する . 方法その1 で指摘したように、MyClass 自体はクラスではなく関数なので、そこからクラスメソッドを呼び出すことはできません。
Python2,3対応版
Python2と3の両方で動作するものを書くには、少し複雑なスキームを使用する必要があります。メタクラスは通常
type
を使えば、実行時に中間的な基底クラスをメタクラスとして動的に作成し、それを
その
のベースクラスとして使用します。
Singleton
の基底クラスです。次に示すように、説明するのはやるより難しいです。
# works in Python 2 & 3
class _Singleton(type):
""" A metaclass that creates a Singleton base class when called. """
_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 Singleton(_Singleton('SingletonMeta', (object,), {})): pass
class Logger(Singleton):
pass
この方法の皮肉な点は、メタクラスを実装するためにサブクラス化を利用していることです。考えられる利点としては、純粋なメタクラスとは異なり
isinstance(inst, Singleton)
が返されます。
True
.
訂正履歴
別の話題ですが、すでにお気づきかもしれませんが、元の投稿にあるベースクラスの実装は間違っています。
_instances
にする必要があります。
クラスで参照される
を使用する必要があります。
super()
でないと
再帰
で、そして
__new__
は、実際には静的メソッドであり
にクラスを渡します。
はクラスメソッドではなく、実際のクラスは
は作成されていません。
が呼び出されたとき、まだ これらのことは、メタクラスの実装でも同じように当てはまります。
class Singleton(object):
_instances = {}
def __new__(class_, *args, **kwargs):
if class_ not in class_._instances:
class_._instances[class_] = super(Singleton, class_).__new__(class_, *args, **kwargs)
return class_._instances[class_]
class MyClass(Singleton):
pass
c = MyClass()
クラスを返すデコレータ
もともとコメントを書いていたのですが、長くなりすぎたので、ここに追記します。 方法その4 は他のデコレーターバージョンよりも優れていますが、シングルトンに必要なコードよりも多く、何をするものなのかが明確ではありません。
主な問題は、クラスがそれ自身のベースクラスであることに起因しています。まず、あるクラスが、ほぼ同じ名前の、そのクラスの
__class__
属性はどうでしょうか?また、このことは
ベースクラスの同名のメソッドを呼び出すメソッドは、すべて
と
super()
というのも、これらは再帰的に処理されるからです。つまり、あなたのクラスでは
__new__
を必要とするクラスから派生させることはできません。
__init__
が呼び出されます。
シングルトンパターンを使用する場合
あなたのユースケースは 良い例の一つ シングルトンを使いたいということです。あなたはコメントの中で "私にとってロギングは常にシングルトンの自然な候補に見えましたと述べています。 全くその通り .
シングルが悪いと言われるとき、最も多いのは、その理由が 暗黙の共有状態 . 一方、グローバル変数やトップレベルモジュールのインポートは 明示的 は状態を共有し、それ以外の渡されるオブジェクトは一般にインスタンス化されます。これは良い点です。 ただし、次の2つの例外があります。 .
1つ目は、様々な場所で言及されることですが、シングルトーンの場合です。 定数 . グローバル定数、特に列挙型の使用は広く受け入れられており、何があっても大丈夫なので、正気だと考えられています。 どのユーザーも、他のユーザーのためにそれを台無しにすることはできません。 . これは、定数のシングルトンにも同様に当てはまります。
2つ目の例外は、あまり言及されませんが、その逆で、シングルトンが 単なるデータシンク 直接または間接的に)データソースではありません。これが、ロガーがシングルトンにとって「自然な使い方」であると感じる理由です。様々なユーザーが ロガーを変更しない 他のユーザが気になるような方法で 共有状態ではない . このことは、シングルトンパターンに対する主要な議論を否定し、シングルトンパターンを合理的な選択とするのは、その 使いやすさ というタスクがあります。
以下は、引用です。 http://googletesting.blogspot.com/2008/08/root-cause-of-singletons.html :
さて、シングルトンには1種類だけ、問題ないものがあります。それは、到達可能なオブジェクトがすべてイミュータブル(不変)であるシングルトンです。すべてのオブジェクトがイミュータブルであれば、シングルトンはグローバルな状態を持ちません。しかし、この種のシングルトンをミュータブルなものに変えるのはとても簡単で、とても滑りやすい坂道です。だから、私はこの種のシングルトンには反対です。enumerationにstateを入れなければ大丈夫なので、ご遠慮ください)
他の種類のシングルトンは、コードの実行に影響を与えないもので、副作用がありません。ロギングがその典型例です。これはシングルトンやグローバルなステートを含んでいます。あるロガーが有効であるかどうかに関わらず、アプリケーションの動作は変わらないので、許容できます(あなたを傷つけないという意味で)。ここでの情報の流れは一方通行です。アプリケーションからロガーへです。ロガーからアプリケーションに情報が流れないので、ロガーがグローバル状態であっても、 ロガーは許容されます。しかし、一般に、ロガーは状態によって変化しますが、有害ではありません。
関連
-
python call matlab メソッドの詳細
-
任意波形を生成してtxtで保存するためのPython実装
-
[解決済み】 AttributeError("'str' object has no attribute 'read'")
-
[解決済み] Pythonには文字列の'contains'サブストリングメソッドがありますか?
-
[解決済み] Pythonで現在時刻を取得する方法
-
[解決済み] Pythonで辞書に新しいキーを追加するにはどうすればよいですか?
-
[解決済み] Pythonで2つのリストを連結する方法は?
-
[解決済み] Pythonで例外を手動で発生(スロー)させる
-
[解決済み] 静的クラスとシングルトンパターンの違い?
-
[解決済み】Pythonに三項条件演算子はありますか?
最新
-
nginxです。[emerg] 0.0.0.0:80 への bind() に失敗しました (98: アドレスは既に使用中です)
-
htmlページでギリシャ文字を使うには
-
ピュアhtml+cssでの要素読み込み効果
-
純粋なhtml + cssで五輪を実現するサンプルコード
-
ナビゲーションバー・ドロップダウンメニューのHTML+CSSサンプルコード
-
タイピング効果を実現するピュアhtml+css
-
htmlの選択ボックスのプレースホルダー作成に関する質問
-
html css3 伸縮しない 画像表示効果
-
トップナビゲーションバーメニュー作成用HTML+CSS
-
html+css 実装 サイバーパンク風ボタン
おすすめ
-
PythonによるLeNetネットワークモデルの学習と予測
-
Pythonの学習とデータマイニングのために知っておくべきターミナルコマンドのトップ10
-
[解決済み】numpyの配列連結。"ValueError:すべての入力配列は同じ次元数でなければならない"
-
[解決済み】IndexError: invalid index to scalar variableを修正する方法
-
[解決済み] TypeError: 'DataFrame' オブジェクトは呼び出し可能ではない
-
[解決済み】Python - "ValueError: not enough values to unpack (expected 2, got 1)" の修正方法 [閉店].
-
[解決済み】 TypeError: += でサポートされていないオペランド型: 'int' および 'list' です。
-
[解決済み】Flaskのテンプレートが見つからない【重複あり
-
[解決済み] シングルトンを定義するシンプルでエレガントな方法はありますか?重複
-
[解決済み] メタクラスの(具体的な)使用例とは?