45

Python 2 では避けるsuperべきだとよく言われます。 Python 2での の使用で、次の例のようなすべての引数を指定しない限り、期待どおりに動作しないことがわかりました。super

super(ThisClass, self).some_func(*args, **kwargs)

これは を使用する目的に反しているように思えます。super()簡潔でもなければ、 よりもはるかに優れているわけでもありませんTheBaseClass.some_func(self, *args, **kwargs)。ほとんどの場合、メソッド解決の順序は遠いおとぎ話です。

4

5 に答える 5

45

super()基本クラスのメソッドを呼び出す標準的な方法と見なされるべきではありません。これは Python 3.x でも変わりませんでした。変更された唯一のことは、現在の関数の最初のパラメーターであり、現在定義されているクラスself, clsである標準ケースで引数を渡す必要がないことです。selfcls

を実際にいつ使用するかという質問に関してsuper()、私の答えは次のとおりです。super()私は個人的に、便利な多重継承を避けるようにしています。

編集:私がかつて遭遇した実生活の例:run()メソッドを定義するいくつかのクラスがあり、そのうちのいくつかには基本クラスがありました。以前super()は継承されたコンストラクターを呼び出していましたが、単一の継承のみを使用していたため、それが問題になるとは思いませんでした。

class A(object):
    def __init__(self, i):
        self.i = i
    def run(self, value):
        return self.i * value

class B(A):
    def __init__(self, i, j):
        super(B, self).__init__(i)
        self.j = j
    def run(self, value):
        return super(B, self).run(value) + self.j

これらのクラスがいくつかあり、すべてが個別のコンストラクター プロトタイプを持ち、run().

ここで、これらすべてのクラスにロギングなどの機能を追加したいと考えました。追加の機能では、これらすべてのクラスで追加のメソッドを定義する必要がありましたinfo()。元のクラスに侵入したくはありませんでしたが、元のクラスから継承する 2 番目のクラス セットを定義し、info()メソッドを追加して、実際のログを提供する mix-in から継承しました。今、super()コンストラクターで使用できなくなったため、直接呼び出しを使用しました。

class Logger(object):
    def __init__(self, name):
        self.name = name
    def run_logged(self, value):
        print "Running", self.name, "with info", self.info()
        return self.run(value)

class BLogged(B, Logger):
    def __init__(self, i, j):
        B.__init__(self, i, j)
        Logger.__init__("B")
    def info(self):
        return 42

ここで物事は機能しなくなります。基本クラスのコンストラクターでのsuper()呼び出しが突然 を呼び出しLogger.__init__()BLoggedそれについて何もできません。実際には、super()呼び出しB自体を削除する以外に、これを機能させる方法はありません。

[別の編集:ここと他の回答の下のすべてのコメントから判断すると、私は自分の主張をしていないようです。を使用してこのコードを機能させる方法は次のsuper()とおりです。

class A(object):
    def __init__(self, i, **kwargs):
        super(A, self).__init__(**kwargs)
        self.i = i
    def run(self, value):
        return self.i * value

class B(A):
    def __init__(self, j, **kwargs):
        super(B, self).__init__(**kwargs)
        self.j = j
    def run(self, value):
        return super(B, self).run(value) + self.j

class Logger(object):
    def __init__(self, name, **kwargs):
        super(Logger,self).__init__(**kwargs)
        self.name = name
    def run_logged(self, value):
        print "Running", self.name, "with info", self.info()
        return self.run(value)

class BLogged(B, Logger):
    def __init__(self, **kwargs):
        super(BLogged, self).__init__(name="B", **kwargs)
    def info(self):
        return 42

b = BLogged(i=3, j=4)

これを明示的なスーパークラス呼び出しの使用と比較してください。好みのバージョンを決定します。]

super() これと同様の話が、基本クラスのメソッドを呼び出す標準的な方法と見なされるべきではないと私が考える理由です。super()壊れているわけではありません。

于 2011-02-21T13:16:49.493 に答える
33

super()Python2またはPython3では壊れていません。

ブログ投稿からの議論を考えてみましょう:

  • それはそれがするように聞こえるものをしません。

OK、あなたはそれに同意するかもしれないし、同意しないかもしれません、それはかなり主観的です。その時それは何と呼ばれるべきでしたか?super()はスーパークラスを直接呼び出す代わりの名前なので、名前は私にはわかりません。スーパークラスを直接呼び出すことはありません。それがすべてだったとしたら、とにかくそれを行うことができるので、それは無意味になるからです。確かに、それは明らかではないかもしれませんが、必要な場合super()は一般的に明白ではありません。あなたがそれを必要とするならば、あなたはかなり毛深い多重継承をしている。それは明白ではないでしょう。(または、単純なミックスインを実行している場合、ドキュメントを読んでいない場合でも、それは非常に明白であり、期待どおりに動作します)。

スーパークラスを直接呼び出すことができれば、それはおそらくあなたがやることになるでしょう。それはそれを行うための簡単で直感的な方法です。super()それが機能しない場合にのみ機能します。

  • スーパークラスを直接呼び出すこととはうまく調和しません。

はい、それはそれを行うことで問題を解決するように設計されているからです。スーパークラスを直接呼び出すことができるのは、それがどのクラスであるかを正確に知っている場合のみです。たとえば、ミックスインの場合や、クラス階層がめちゃくちゃになっていて、実際に2つのブランチをマージしている場合(これは、すべての使用例の典型的な例ですsuper())。

クラス階層内のすべてのクラスに明確に定義された場所がある限り、スーパークラスの呼び出しは直接機能します。そうしないと機能しません。その場合は、super()代わりに使用する必要があります。これはsuper()、「次のスーパークラス」がMROに従って何であるかを明示的に指定しなくても把握できるという点です。これは、たとえば、次の場合など、常にそれが何であるかがわからないため、常に指定できるとは限らないためです。ミックスインを使用します。

  • 完全に異なるプログラミング言語であるDylanは、一種のlisp-thingyであり、Pythonでは使用できない別の方法でこれを解決します。これは、非常に異なるためです。

ええ わかった?

  • super()あなたのスーパークラスを呼びません。

ええ、あなたはそれを言いました。

  • super()混同して直接電話しないでください。

ええ、あなたもそう言った。

したがって、それに反対する2つの議論があります。1。名前が悪い。2.一貫して使用する必要があります。

それは、それが「壊れている」、または「回避されるべき」という意味ではありません。

于 2011-02-21T14:22:17.477 に答える
3

リンクしている記事は読みましたか?super避けるべきだと結論付けているわけではありませんが、使用する際には注意が必要です。これらの警告は記事に要約されていますが、私は彼らの提案に同意しません.

この記事の要点は、多重継承は面倒になり、作成super者が望むほどには役に立たないということです。ただし、多重継承なしで行うsuperと、さらに複雑になることがよくあります。

多重継承を行っていない場合はsuper、クラスから継承する人なら誰でも単純なミックスインを追加でき、それら__init__が適切に呼び出されるという利点があります。__init__から継承している場合でも、常にスーパークラスの を呼び出し、object残りのすべての引数 (*aおよび**kw) をそれに渡すことを忘れないでください。親クラスから他のメソッドを呼び出すときも を使用しますsuperが、今回は既に知っている適切なシグネチャを使用します (つまり、すべてのクラスで同じシグネチャを持つようにします)。

多重継承を行っている場合は、それよりも深く掘り下げる必要があり、おそらく同じ記事をより注意深く読み直して、注意事項に注意する必要があります。また、親への明示的な呼び出しが よりも優れている可能性があるのは、多重継承中のみですがsuper、特定のシナリオがなければ、使用する必要があるかどうかは誰にもわかりsuperません。

Python 3.xでの唯一の変更点はsuper、現在のクラスを明示的に渡す必要がないselfことです。これをsuper使用すると、親クラスまたは現在のクラスのハードコーディングが不要になるため、これはより魅力的です。

于 2011-02-21T13:24:06.147 に答える
1

@Sven Marnach:

あなたの例の問題は、明示的なスーパークラスの呼び出しB.__init__Logger.__init__BloggedwithinBを混在させることですsuper()。それは機能しません。すべての明示的なスーパークラス呼び出しを使用するかsuper()、すべてのクラスで使用します。使用するときはsuper()、AIthinkを含む関連するすべてのクラスで使用する必要があります。また、あなたの例では、すべてのクラスで明示的なスーパークラス呼び出しを使用できると思います。つまりA.__init__、クラスBで使用できます。

ダイヤモンドの継承がない場合、super()にはあまり利点がないと思います。ただし、問題は、将来ダイヤモンドの継承が発生するかどうかが事前にわからないことです。その場合は、super()とにかく使用するのが賢明です(ただし、一貫して使用することをお勧めします)。そうしないと、後ですべてのクラスを変更しなければならないか、問題が発生することになります。

于 2011-02-21T16:08:57.340 に答える