6

最近、Pythonでシングルトンを作成する方法に関する興味深い議論を読みました。解決策の1つは、装飾されたクラスの代わりにコード内のクラスを定義するトリッキーなデコレータでした

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.__name__ = 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 __name__ is preserved
True    # this is actually the same instance

super(MyClass, self).__init__(text)の内部__init__を使用すると、再帰MyClassに入ると述べられています。

テストしたところ、実際に再帰が発生しました。しかし、私が理解しているように、MyClass継承するobjectので、super(MyClass, self)単にである必要がありますobjectが、それsuper(MyClass, self)__main__.MyClass

再帰が発生する理由を理解するために、ここで何が起こっているのかを段階的に説明していただけますか?

4

1 に答える 1

5

問題は、と書くことで、その時点で参照されているsuper(MyClass, self).__init__(text)クラスに関連するスーパーを使用すると言っていることです。ただし、デコレータはそれ自体のサブクラスに置き換えられます。したがって、元のメソッドが呼び出されると、実際には実行メソッドを定義するクラスのサブクラスが参照されます。MyClasssuperMyClass__init__MyClass

ステップバイステップで言うと、元のクラス(ソースに記述されている)OrigMyClassと、結果のバージョン(デコレータの後)を呼び出しますDecMyClassMyClass実行中に意味が変わるので、変数として使用します。

  1. __init__でメソッドを定義しますOrigMyClassが、その__init__メソッドはsuper(MyClass, self)ではなく、を呼び出しますsuper(OrigMyClass, self)。したがって、実際に呼び出されるメソッドは、メソッドが呼び出されたときMyClassに何を参照しているかによって異なります。の値は、他の変数と同様に実行時に検索されます。呼び出し内またはメソッド内に配置しても、記述時にたまたま存在するクラスに魔法のようにバインドされることはありません。関数内の変数は、定義されたときではなく、呼び出されたときに評価されます。MyClasssuper__init__

  2. デコレータが実行されます。デコレータは、新しいクラスDecMyClassをのサブクラスとして定義しOrigMyClassます。 を呼び出すをDecMyClass定義します。__init__super(DecMyClass, self)

  3. デコレータが実行された後、名前MyClassはクラスにバインドされDecMyClassます。super(MyClass, self)これは、後で呼び出しが実行されるときに、実行されることを意味することに注意してくださいsuper(DecMyClass, self)

  4. を実行するとMyClass(111)、のオブジェクトがインスタンス化されますDecMyClassDecMyClass.__init__を呼び出しますsuper(DecMyClass, self).__init__。これは実行されOrigMyClass.__init__ます。

  5. OrigMyClass.__init__を呼び出しますsuper(MyClass, self).__init__MyClassを参照しているためDecMyClass、これはと同じsuper(DecMyClass, self).__init__です。しかしDecMyClass、はのサブクラスですOrigMyClass。重要な点は、MyClassを参照しているためDecMyClass、実際にはそれ自体のサブクラスでOrigMyClassスーパーを呼び出しているということです。

  6. したがって、super(DecMyClass, self).__init__再び、を呼び出しOrigMyClass.__init__、それは再びそれ自体を呼び出し、以下同様に無限大になります。

効果はこのコードと同じであり、実行パスがより明確になる可能性があります。

>>> class Super(object):
...     def __init__(self):
...         print "In super init"
...         super(Sub, self).__init__()
>>> class Sub(Super):
...     def __init__(self):
...         print "In sub init"
...         super(Sub, self).__init__()

Superを呼び出すことに注意してくださいsuper(Sub, self)。スーパークラスメソッドを呼び出そうとしていますが、のスーパークラスメソッドを呼び出そうとしていますSub。のスーパークラスSubSuperであるためSuper、独自のメソッドを再度呼び出すことになります。

編集:あなたが提起した名前検索の問題を明確にするために、同じ結果をもたらす別のわずかに異なる例を次に示します。

>>> class Super(object):
...     def __init__(self):
...         print "In super init"
...         super(someClass, self).__init__()
>>> class Sub(Super):
...     def __init__(self):
...         print "In sub init"
...         super(Sub, self).__init__()
>>> someClass = Sub

superこれにより、(最初の引数、ここでは)のクラス引数がsomeClass特別なものではないことが明確になります。superこれは、通常の時間、つまり呼び出しが実行されたときに通常の方法で値が検索される単なる通常の名前です。この例で示されているように、メソッドを定義するときに変数が存在している必要はありません。メソッドを呼び出したときに値が検索されます。

于 2012-08-04T21:08:20.277 に答える