親クラスのメソッドを上書きすることがよくあります。指定されたパラメーターを明示的にリストするか、単にブランケット*args, **kwargs
コンストラクト。 1つのバージョンは他のバージョンより優れていますか?ベストプラクティスはありますか?欠けている(不利な)利点は何ですか?
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
明示的なバリアントの認識される利点
ブランケットバリアントの認識される利点
リスコフ置換原理
一般に、メソッドシグネチャの派生型が異なることは望ましくありません。これは、派生型の使用を交換する場合に問題を引き起こす可能性があります。これは、多くの場合、 Liskov Substitution Principle と呼ばれます。
明示的署名の利点
同時に、すべてのメソッドが*args
、**kwargs
の署名を持つことは正しいとは思いません。明示的な署名:
可変長引数と結合
カップリングを適切に行うために可変長引数を間違えないでください。親クラスと派生クラスの間には一定量の凝集性がなければなりません。そうでなければ、それらは互いに関連しません。関連するコードが結束のレベルを反映する結合をもたらすことは正常です。
可変長引数を使用する場所
可変長引数の使用は、最初のオプションではありません。次のような正当な理由がある場合に使用する必要があります。
何かおかしいですか?
多くの引数をとるメソッドまたは異なるシグネチャを持つ派生メソッドを作成することが多い場合は、コードの編成方法に大きな問題がある可能性があります。
私の選択は次のとおりです。
class Child(Parent):
def save(self, commit=True, **kwargs):
super(Child, self).save(commit, **kwargs)
# more logic
*args
および**kwargs
からコミット引数にアクセスすることを避け、Parent:save
の署名が変更された場合(たとえば、新しいデフォルト引数を追加する場合)に安全を保ちます。
更新:この場合、* argsがあると、新しい位置引数が親に追加された場合に問題が発生する可能性があります。 **kwargs
のみを保持し、デフォルト値を持つ新しい引数のみを管理します。伝播するエラーを回避します。
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
を数回呼び出すのは、引数やkwargsがない場合はかなりうんざりするからです。
価値があるものとしては、これはかなりの数のPython libsとフレームワーク(Django、Tornado、Requests、Markdown、いくつか挙げれば))でも推奨される方法です。そのようなことについて、私はこのアプローチが非常に広く普及していることを暗示しているだけです。
本当の答えではなく、補足的な注意:親クラスのデフォルト値が子クラスに伝播されることを本当に確認したい場合は、次のようなことができます:
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)
しかし、これは非常に見苦しいと認めなければならず、本当に必要だと感じた場合にのみ使用します。
オートコンプリートを使用すると、関数呼び出し中に関数のメソッドシグネチャを確認できるため、明示的な引数を好みます。
他の答えに加えて:
可変引数を持つと、親が子から「分離」される場合がありますが、作成されたオブジェクトと親の間にカップリングが作成されます。アプリケーション内に複数のオブジェクトを作成する可能性があるため)
デカップリングを探しているなら、 composition over inheritance を見てください