8

これが私が書こうとしていたコードです:

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().foosuper().foo

"SystemError: super(): no arguments". 

誰かがなぜそうなのか説明できますか?

4

1 に答える 1

26

を呼び出すときにorを使用しないでください。self.__class__type(self)super()

Python 3 では、引数なしの への呼び出しは(class のメソッド内で);super()と同等です。クラスの明示的な命名に注意してください。Python コンパイラは、定義されている現在のクラスを参照するクロージャ セルを、引数なしで使用するメソッドに追加します ( Why is Python 3.x's super() magic?を参照)。super(B, self)B__class__super()

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

要約すると、Python 3 では次のようになります。

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 番目の形式のみに固執しています。

メソッドでは、MRO を検索する明示的なクラスを渡す必要がありますbind_foo()。Python コンパイラは、新しい replacement をバインドするときに使用されるクラスをここで判断できないためです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

(no ) を使用して Python にクロージャ セルを提供させることもできますが、それはここではなくへの参照になります。new をバインドするときは、MRO でオーバーライドされたメソッドの検索を代わりに at から開始する必要があります。__class__selfACfooC

Dからサブクラス化して class を作成すると、再びC問題が発生することに注意してください。その場合の最善の策は、明示的なクラス参照を使用して呼び出すことです。ここ(いいえ) はうまくいきます:bind_foo()super()DCbind_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()代わりに呼び出すことができます。

于 2013-11-04T20:07:03.290 に答える