1. ホーム
  2. python

[解決済み] super().method() と super(self.__class__,self).method() の違いについて [重複]。

2022-02-07 16:52:57

質問

以下は、私が書こうとしていたコードです。

class A(object):
    def bind_foo(self):
        old_foo = self.foo
        def new_foo():
            old_foo()
            #super().foo()
            super(self.__class__,self).foo()

        self.foo = new_foo

    def __init__(self):
        print("A __init__")          

    def foo(self):
        print("A foo")

class B(A):
    def __init__(self):
        print("B __init__")
        super().__init__()

    def foo(self):
        print("B foo")
        super().foo()

class C(A):
    def __init__(self):
        print("C __init__")
        super().__init__()
        super().bind_foo()

    def foo(self):
        print("C foo")    

b  = B()
b.foo()

c = C()
c.foo()

BクラスとAクラスは期待される動作、つまり、私が呼び出したときに b.foo() を呼び出す。 a.foo() と同様に super() . クラスCは、子Bと親Aの動作を模倣しようとしていますが、今回は、明示的に super().foo() は子クラスで、親クラスの foo() が呼び出されます。期待通りに動作しています。

しかし、私がよく理解していないのは、以下のように A.bind_foo を使用する必要があります。 super(self.__class__,self).foo() よりも super().foo . super().foo が与える。

"SystemError: super(): no arguments". 

なぜそうなるのか、誰か説明してください。

どのように解決するのですか?

あなたは ではなく 使用 self.__class__ または type(self) を呼び出したとき super() .

Python 3 では super() 引数なしは、以下のものと同じです。 super(B, self) (クラス上のメソッド内で B ); 注意すべきは 明示的な という名前を付けています。Pythonコンパイラは __class__ を使用するメソッドには、クロージャセル super() 引数なしで ( Python 3.xのsuper()はなぜ魔法なのですか? を参照する)。 現在のクラス が定義されています。

を使用する場合 super(self.__class__, self) または super(type(self), self) を使用すると、サブクラスがそのメソッドを呼び出そうとしたときに無限再帰例外が発生します。 self.__class__ 派生クラス であり、オリジナルではない。参照 派生クラスでsuper()を呼び出すとき、self.__class__を渡してもいいのでしょうか?

ということで、まとめると、Python3では。

class B(A):
    def __init__(self):
        print("B __init__")
        super().__init__()

    def foo(self):
        print("B foo")
        super().foo()

が等しい。

class B(A):
    def __init__(self):
        print("B __init__")
        super(B, self).__init__()

    def foo(self):
        print("B foo")
        super(B, self).foo()

が、前者を使った方が、同じことを繰り返さずに済みます。

Python 2 では、2 番目の形式である のみ .

あなたのために bind_foo() Python コンパイラは、新しい置換オブジェクトをバインドするときにどのクラスが使われるかを判断できないからです。 foo :

def bind_foo(self, klass=None):
    old_foo = self.foo
    if klass is None:
        klass = type(self)

    def new_foo():
        old_foo()
        super(klass, self).foo()

    self.foo = new_foo

あなた 可能 使用 __class__ (いいえ self への参照となり、Pythonはクロージャーセルを提供します。 A ではなく C をここに示します。新しい foo で検索を開始し、MRO でオーバーライドされたメソッドを検索したい場合。 C の代わりに

ここで、もしあなたがクラス D をサブクラス化したものです。 C うまくいかない また というのは、今、あなたは bind_foo() を呼び出し、次に super()D でなく C を開始点とします。その場合、最善の策は bind_foo() を使用しています。 明示的 クラスの参照になります。ここでは __class__ (いいえ self. を使用するとうまくいきます。

class C(A):
    def __init__(self):
        print("C __init__")
        super().__init__()
        self.bind_foo(__class__)

を使用した場合と同じ動作になります。 super() を引数なしで参照すると 現在の クラス、つまりメソッドを定義しているクラスです。 __init__ に渡されます。 super() を作成します。 new_foo() のクラス定義の中で直接定義されているかのように振る舞います。 C .

を呼び出す意味がないことに注意してください。 bind_foo()super() ここではオーバーライドしていないので、単に self.bind_foo() の代わりに