Pythonの継承は「is-a」の継承スタイルですか、それともコンポジションスタイルですか?


10

Pythonが複数の継承を許可する場合、Pythonの慣用的な継承はどのように見えますか?

Javaのように単一継承の言語では、あるオブジェクトが別のオブジェクトの「is-a」であり、オブジェクト間でコードを共有したい場合(親オブジェクトから子オブジェクトまで)は、継承が使用されます。たとえば、それDogAnimal

public class Animal {...}
public class Dog extends Animal {...}

しかし、Pythonは多重継承をサポートしているため、他の多くのオブジェクトを組み合わせてオブジェクトを作成できます。以下の例を検討してください。

class UserService(object):
    def validate_credentials(self, username, password):
        # validate the user credentials are correct
        pass


class LoggingService(object):
    def log_error(self, error):
        # log an error
        pass


class User(UserService, LoggingService):
    def __init__(self, username, password):
        self.username = username
        self.password = password

    def authenticate(self):
        if not super().validate_credentials(self.username, self.password):
            super().log_error('Invalid credentials supplied')
            return False
         return True

これはPythonでの多重継承の許容できる使用法ですか?継承とは、あるオブジェクトが別のオブジェクトの "is-a"である場合ではなくUserUserServiceおよびで構成されるモデルを作成しますLoggingService

データベースまたはネットワーク操作のすべてのロジックUserは、それらをUserServiceオブジェクトに配置し、にログインするためのすべてのロジックを保持することにより、モデルとは別に保つことができますLoggingService

このアプローチのいくつかの問題は次のとおりです。

  • これは神オブジェクトを作成しますか?以来Userからの継承、またはで構成され、UserServiceそしてLoggingServiceそれは本当に、単一責任の原則に従っていますか?
  • parent / next-in-lineオブジェクトのメソッドにアクセスするには(たとえば、UserService.validate_credentialsを使用する必要がありますsuper。これにより、このメソッドを処理するオブジェクトを確認することが少し難しくなり、次のように明確ではありません。 、のUserServiceようなものをインスタンス化して行うself.user_service.validate_credentials

上記のコードを実装するためのPython的な方法は何でしょうか?

回答:


9

Pythonの継承は「is-a」の継承スタイルですか、それともコンポジションスタイルですか?

Pythonは両方のスタイルをサポートしています。あなたは、構成の関係を示しています。つまり、ユーザーは、あるソースからのロギング機能と別のソースからの資格情報検証を持っています。LoggingServiceそしてUserService塩基がミックスインされている:彼らは、機能性を提供し、自分でインスタンス化することを意図していません。

タイプでミックスインを構成することにより、ログを記録できるユーザーがいますが、そのユーザーは独自のインスタンス化機能を追加する必要があります。

これは、単一の継承に固執できないという意味ではありません。Pythonもそれをサポートしています。開発の能力が多重継承の複雑さによって妨げられている場合は、より快適に感じるか、トレードオフの価値があると考える設計のポイントに到達するまで、それを回避できます。

これは神オブジェクトを作成しますか?

ロギングはいくぶん正接しているように見えます-Pythonにはロガーオブジェクトを備えた独自のロギングモジュールがあり、規則はモジュールごとに1つのロガーがあるということです。

ただし、ロギングモジュールは脇に置いておきます。おそらくこれは単一の責任に違反しますが、おそらく、特定のコンテキストでは、ユーザーを定義することが重要です。責任の具体化は物議を醸す可能性があります。しかし、より広い原則は、Pythonはユーザーが決定を下すことを可能にするということです。

はっきりしていませんか?

super同じ名前の関数の内部からメソッド解決順序(MRO)で親に委任する必要がある場合にのみ必要です。親のメソッドへの呼び出しをハードコーディングする代わりにそれを使用することがベストプラクティスです。ただし、親をハードコードしない場合は、は必要ありませんsuper

ここの例では、必要なのはだけですself.validate_credentialsselfあなたの観点から、より明確ではありません。どちらもMROに従います。必要に応じて、それぞれを使用します。

を呼び出した場合はauthenticatevalidate_credentials代わりに、super再帰エラーを回避するためにを使用する(または親をハードコードする)必要があります。

代替コードの提案

したがって、セマンティクスが(ロギングのように)OKであると仮定すると、クラスでUser次のようになります。

    def validate_credentials(self): # changed from "authenticate" to 
                                    # demonstrate need for super
        if not super().validate_credentials(self.username, self.password):
            # just use self on next call, no need for super:
            self.log_error('Invalid credentials supplied') 
            return False
        return True

1
同意しません。継承は常に、クラスのパブリックインターフェイスのコピーをそのサブクラスに作成します。これは「has-a」関係ではありません。これはサブタイピングであり、単純明快であるため、説明されているアプリケーションには不適切です。
ジュール

@Julesあなたは何に反対しますか?私は実証可能な多くのことを述べ、論理的に続く結論を出しました。あなたはありますが、言うとき、誤った「継承は常にそのサブクラス内のクラスのパブリックインターフェイスのコピーを作成します。」Pythonでは、コピーはありません。メソッドは、C3アルゴリズムのメソッド解決順序(MRO)に従って動的に検索されます。
アーロンホール

1
重要なのは、実装がどのように機能するかの特定の詳細ではなく、クラスのパブリックインターフェイスがどのように見えるかについてです。例の場合、Userオブジェクトは、それらのインターフェースだけでなく、で定義されたメンバーを持っているUserクラスだけでなく、中に定義されたものUserServiceLoggingService。これ「has-a」関係ではありません。これ、パブリックインターフェイスがコピーされるためです(直接コピーではなく、スーパークラスのインターフェイスへの間接参照によって)。
ジュール

Has-aは構成を意味します。ミックスインはコンポジションの一種です。Userクラス UserServiceやLoggingServiceではありませんが、その機能を備えいます。Pythonの継承は、Javaの継承とは想像以上に異なると思います。
アーロンホール

@AaronHallあなたは単純化しすぎています(これは、私が偶然見つけた他の回答と矛盾する傾向があります)。サブタイプの関係の観点から見ると、UserはUserServiceとLoggingServiceの両方です。ここでの精神は、ユーザーがそのような機能を持つように機能を構成することです。一般的に、ミックスインは多重継承で実装する必要はありません。ただし、これはPythonで通常行う方法です。
コアダンプ2016年

-1

複数のスーパークラスを許可するという事実を除いて、Pythonの継承はJavaの継承と実質的に変わりません。つまり、サブクラスのメンバーは、それぞれのスーパータイプのメンバーでもあります[1]。Pythonがダックタイピングを使用するという事実も違いはありません。サブクラスにはスーパークラスのすべてのメンバーが含まれているため、これらのサブクラスを使用できるすべてのコードで使用できます。コンポジションを使用することで多重継承が効果的に実装されるという事実はレッドヘリングです。あるクラスのプロパティを別のクラスに自動コピーすることが問題であり、コンポジションを使用するか、単にメンバーの想定方法を魔法のように推測するかは問題ではありません働くこと:それらを持っていることは間違っています。

はい、これは単一の責任に違反します。これは、オブジェクトが行うように設計されたものの論理的な一部ではないアクションを実行する能力をオブジェクトに与えるからです。はい、それは本質的に同じことを言う別の方法である「神」オブジェクトを作成します。

Pythonでオブジェクト指向システムを設計する場合、Javaの設計図書で述べられているのと同じ格言が適用されます。継承よりも構成を優先します。同じことは、(ほとんど[2])多重継承を持つ他のシステムにも当てはまります。

[1]:これは「is-a」関係と呼んでもかまいませんが、私は個人的にはこの言葉が好きではありません。現実世界のモデリングのアイデアを示唆しており、オブジェクト指向モデリングは現実世界と同じではありません。

[2]:C ++についてはよくわかりません。C ++は「プライベート継承」をサポートします。これは、継承されたクラスのパブリックメンバーを使用するときに、フィールド名を指定する必要のない基本的な構成です。クラスのパブリックインターフェイスにはまったく影響しません。使うのは好きではありませんが、しない理由はありません。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.