web-dev-qa-db-ja.com

Python-2.xでsuper()は壊れていますか?

superavoided in Python 2.私はsuper in Python 2この例のようなすべての引数を指定しない限り、期待どおりに動作しないこと:

_super(ThisClass, self).some_func(*args, **kwargs)
_

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

41
Matt Joiner

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

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()メソッドを追加し、実際のロギングを提供するミックスインから継承しました。これで、コンストラクタで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はそれについて何もできません。 B自体のsuper()呼び出しを削除することを除いて、実際にこの作業を行う方法はありません。

[Another Edit:ここと他の回答の下のすべてのコメントから判断すると、私は自分の主張を述べていないようです。 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()が壊れているわけではありません。

42
Sven Marnach

super()は壊れていません。Python 2またはPython 3。

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

  • それはそれがするように、それが思うようにはしません。

OK、あなたはそれに賛成または反対するかもしれません、それはかなり主観的です。それでは何と呼ばれるべきでしたか? super()は、スーパークラスを直接呼び出すための置換であるため、名前は私には問題ないと思われます。スーパークラスを直接呼び出すことはありません。それがすべてである場合、とにかくそれを行うことができるので、それは無意味だからです。確かに、それは明らかではないかもしれませんが、super()が必要な場合は一般に明らかではありません。必要な場合は、かなり複雑な多重継承を実行しています。明らかではありません。 (または、単純なmixinを実行している場合は、ドキュメントを読まなくても予想どおりに動作し、期待どおりに動作します)。

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

  • これは、スーパークラスを直接呼び出すこととうまく適合しません。

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

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

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

えーOK?

  • super()はスーパークラスを呼び出しません。

ええ、あなたはそれを言った。

  • super()と直接呼び出しを混在させないでください。

ええ、あなたもそれを言った。

つまり、2つの引数があります。1。名前が間違っています。2。一貫して使用する必要があります。

それは、「壊れている」、または「回避する」必要があることを意味しません。

31
Lennart Regebro

あなたはあなたの投稿でそれを暗示しているようです

_def some_func(self, *args, **kwargs):
    self.__class__.some_func(self, *args, **kwargs)
_

無限再帰ではありません。それは、superの方が正しいでしょう。

また、はい、すべての引数をsuper()に渡す必要があります。これは、確認したいすべての数字を渡さない限り、max()が期待どおりに機能しないという不満に少し似ています。

ただし、3.xでは、必要な引数が少なくなります。super().foo(*args, **kwargs)の代わりにsuper(ThisClass, self).foo(*args, **kwargs)を実行できます。


とにかく、スーパーを避けるべき状況についてはよくわかりません。その動作は、MIが関与している場合にのみ「奇妙」であり、MIが関与している場合、super()は基本的に正しい解決のための 唯一の希望 です。単一継承では、SuperClass.foo(self, *args, **kwargs)よりわずかに冗長であり、何も変わりません。

この種のMIは回避する価値があるとSvenに同意すると思いますが、superを回避する価値があるとは思いません。クラスが継承されることになっている場合、superを使用すると、クラスのユーザーはMIが動作することを期待できます。

6

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

この記事の主なポイントは、多重継承が乱雑になる可能性があり、superは作成者が望むほど役に立たないということです。ただし、superなしで複数の継承を行うことは、多くの場合さらに複雑です。

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

複数の継承を行っている場合は、それよりも深く掘り下げる必要があります。おそらく、警告に気付くために同じ記事をより注意深く読む必要があります。また、親への明示的な呼び出しのほうがsuperよりも優れているかもしれないが、特定のシナリオがなければsuperを使用すべきか、ありません。

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

3
Rosh Oxymoron

@Sven Marnach:

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

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

1