61

*args, **kwargs親クラスのメソッドを上書きすることがよくありますが、特定のパラメーターを明示的にリストするか、ブランケットコンストラクトを使用するかを決めることができません。あるバージョンは他のバージョンよりも優れていますか? ベストプラクティスはありますか? 私が見逃している(不)利点は何ですか?

class Parent(object):

    def save(self, commit=True):
        # ...

class Explicit(Parent):

    def save(self, commit=True):
        super(Explicit, self).save(commit=commit)
        # more logic

class Blanket(Parent):

    def save(self, *args, **kwargs):
        super(Blanket, self).save(*args, **kwargs)
        # more logic

明示的なバリアントの利点

  • より明示的 (Zen of Python)
  • 把握しやすい
  • 簡単にアクセスできる関数パラメータ

ブランケットバリアントのメリット

  • もっとドライ
  • 親クラスは簡単に交換可能
  • 親メソッドのデフォルト値の変更は、他のコードに触れることなく伝播されます
4

6 に答える 6

43

リスコフ置換原理

一般に、メソッドのシグネチャが派生型で異なることは望ましくありません。これは、派生型の使用を交換する場合に問題を引き起こす可能性があります。これは、しばしばリスコフの置換原理と呼ばれます。

明示的署名の利点

同時に、すべてのメソッドが , の署名を持つことは正しくないと思い*argsます**kwargs。明示的な署名:

  • 適切な引数名を使用してメソッドを文書化するのに役立ちます
  • どの引数が必要で、どの引数がデフォルト値を持つかを指定することにより、メソッドを文書化するのに役立ちます
  • 暗黙の検証を提供します (必要な引数が欠落していると、明らかな例外がスローされます)

可変長引数とカップリング

可変長の引数を適切な結合方法と間違えないでください。親クラスと派生クラスの間には、ある程度のまとまりがなければなりません。そうでなければ、それらは互いに関連していません。関連するコードが結束のレベルを反映する結合になるのは正常です。

可変長引数を使用する場所

可変長引数の使用は、最初のオプションではありません。次のような正当な理由がある場合に使用する必要があります。

  • 関数ラッパー (つまり、デコレータ) を定義します。
  • パラメトリック多相関数の定義。
  • 取ることができる引数が実際に完全に可変である場合 (例: 一般化された DB 接続関数)。DB 接続関数は通常、単一引数形式と複数引数形式の両方で、さまざまな形式の接続文字列を取ります。データベースごとに異なるオプション セットもあります。
  • ...

あなたは何か間違ったことをしていますか?

多くの引数をとるメソッドや異なる署名を持つ派生メソッドを頻繁に作成している場合は、コードの編成方法に大きな問題がある可能性があります。

于 2013-02-08T10:54:06.427 に答える
8

Child が署名を保持することが確実な場合は、明示的なアプローチが望ましいことは確かですが、Child が署名を変更する場合は、個人的には両方のアプローチを使用することを好みます。

class Parent(object):
    def do_stuff(self, a, b):
        # some logic

class Child(Parent):
    def do_stuff(self, c, *args, **kwargs):
        super(Child, self).do_stuff(*args, **kwargs)
        # some logic with c

このように、署名の変更は子で非常に読みやすく、元の署名は親で非常に読みやすいです。

私の意見では、これは複数の継承がある場合にも優れた方法ですsuper。なぜなら、args と kwargs がない場合に数回呼び出すのは非常に嫌だからです。

価値のあることとして、これはかなりの数の Python ライブラリとフレームワーク (Django、Tornado、Requests、Markdown など) で推奨される方法でもあります。そのようなことに基づいて選択するべきではありませんが、このアプローチが非常に広まっていることを暗示しているだけです。

于 2013-02-06T13:40:58.060 に答える
4

実際には答えではありませんが、補足事項です。親クラスのデフォルト値が子クラスに確実に伝達されるようにしたい場合は、次のようにすることができます。

class Parent(object):

    default_save_commit=True
    def save(self, commit=default_save_commit):
        # ...

class Derived(Parent):

    def save(self, commit=Parent.default_save_commit):
        super(Derived, self).save(commit=commit)

しかし、これはかなり醜いように見えることを認めなければならず、本当に必要だと感じた場合にのみ使用します。

于 2013-02-10T13:11:04.073 に答える
4

オート コンプリートを使用すると、関数呼び出し中に関数のメソッド シグネチャを確認できるため、私は明示的な引数を好みます。

于 2013-02-07T19:16:33.610 に答える