Pythonでのシングルモデル実現

3951 ワード

原文の住所:http://whosemario.github.io/2016/01/22/pattern-singleton/
Pythonを使用してシングルモデルを実現することはとても面白いことです.この過程でPythonを見直して、クラス(Build Class)を作成する時も、インスタンスを構築する時も、それらのmagic methodを呼び出しました.同時に私もPythonの単一の例のモジュールが多様を実現すると言いたいですが、いくつかの実装には問題があります.場合によってはシステムのいくつかの機能に影響を与えてはいけません.
1.Pythonのmetaclassを利用して一例を実現する.
まず、実現するための最も本格的なPython Singletonの実現方法を考えます.metaclassはPythonの中でとても珍しいものです.Pythonはクラスの構築過程でメタクラスを探しています.デフォルトはtypeです.私たちがクラスで定義したmetaclassなら、Pythonはクラスの構築にこのmetaclassを使います.上の説明は主に以下のコード論理に反映されています.
# ceval.c
static PyObject *  
build_class(PyObject *methods, PyObject *bases, PyObject *name) {
    PyObject *metaclass = NULL, *result, *base;

    // 0.              __metaclass__
    if (PyDict_Check(methods))
          metaclass = PyDict_GetItemString(methods, "__metaclass__");
    if (metaclass != NULL)
          Py_INCREF(metaclass);
    else if (PyTuple_Check(bases) && PyTuple_GET_SIZE(bases) > 0) {
          // 1.     __class__  metaclass
          base = PyTuple_GET_ITEM(bases, 0);
          metaclass = PyObject_GetAttrString(base, "__class__");
          if (metaclass == NULL) {
              PyErr_Clear();
              // 2.  ob_type
              metaclass = (PyObject *)base->ob_type;
              Py_INCREF(metaclass);
          }
     }
     else {
          // 3.           ,    global __metaclass__
          PyObject *g = PyEval_GetGlobals();
          if (g != NULL && PyDict_Check(g))
              metaclass = PyDict_GetItemString(g, "__metaclass__");
          if (metaclass == NULL)
              // 4.  PyClass_Type
              metaclass = (PyObject *) &PyClass_Type;
          Py_INCREF(metaclass);
      }
  ......
metaclassを作成したら、対応するクラスを作成し、作成処理のクラスを初期化します.クラスの作成は主にtype_にあります.callは実現しています.私たちはこの文章でtype_について大体言及しました.callの中の部分のロジックは、主にtp_に関連しています.newとtp_initその後、初期化クラスでPyObject_を呼び出します.Callメソッド.PyObject_Callメソッドはmetaclassのtp_を呼び出します.call方法は、このような対応例を返しますので、一例のモードを実現するために、tp_でも大丈夫です.コールという方法で手足を作ります.
  class Singleton(type):
        _instance = {}
        def __call__(cls, *args, **kwargs):
              if cls not in Singleton._instance:
                  Singleton._instance[cls] = type.__call__(cls, *args, **kwargs)
              return Singleton._instance[cls]

   class A(object):
         __metaclass__ = Singleton

    a1 = A()
    a2 = A()
    print id(a1) == id(a2)
上記の例a 1とインスタンスa 2のidは同じです.例外コールは上の所でもtp_コール
2.__を利用するnew_一例モードを実現する
もう一つの方法はtp_にあります.newの場合は、このような作成されているかどうかを確認し、作成したら直接に対応例を返します.
 class Singleton(object):
     _instance = None
     def __new__(cls, *args, **kwargs):
           if Singleton._instance is None:
               Singleton._instance = object.__new__(cls, *args, **kwargs)
           return Singleton._instance

 class A(Singleton):
     def __init__(self):
           print "init A"

  a1 = A()
  a2 = A()
  print id(a1) == id(a2)
上記のような方法は単例でも実現できますし、リスクもあります.1.サブクラスが重積載されたら_u unew_方法は、父の種類の_u u u unew_この方法は機能しません.2.上のコードを実行すると、2回の「init A」がプリントされます.この場合、tp_newは2回呼び出されます.2回目は_のためです.instanceはNoneに等しくなくて、直接帰ってきます.同じtp_initも二回呼び出されます.毎回Aの__u uを呼び出します.init_方法.もしうっかりしたらグウにいますinit_いくつかの初期化ロジックを実行すると、各インスタンスを構築するたびに初期化されます.
3.修飾器を利用した単例モードの実現
 _instance = {}
 def Singleton(cls):
      def wrapper(*args, **kwargs):
           if cls not in _instance:
               _instance[cls] = cls(*args, **kwargs)
           return _instance[cls]
      return wrapper

  @Singleton
  class A(object):
        def __init__(self):
             print "init"

   a = A() 
   b = A() 
   print id(a)
   print id(b)
   print type(A)
上のコードを実行すると、すべてが合理的に見えます.「init」も一回だけ印刷しました.最後の行の出力を見ると、Aのタイプはfunctionです.はい、明らかに間違っていません.私達は修飾器で飾りました.しかし、Aのタイプが変わると、どのようなマイナス効果が生じるかは把握しにくいです.工事が拡大するにつれて、Aはどのタイプの開発者なのかがわからなくなり、多くの問題が出てきて、位置づけが難しいです.
4.まとめ
上の3つの方法は実は全部単例のモードを実現することができて、個人の経験と好みにとって、第1種は最も良いです.