BackEnd

Pythonのポリモーフィズム、ABCにするか? プロトコルにするか?

投稿日:

はじめに

Pythonでポリモーフィズムを実現する方法として、ABC(Abstract Base Classes) または Protocolがあります。個人的には、最近はProtocolを選ぶことの方が多いのですが、ちゃんと比較してみたことがなかったので、調べて比較してみました。

ズバリ、ABCとProtocolは何が違うか?

ざっくり、このような目的・使いどころです。

  • ABC (Abstract Base Classes)
    • その名の通り、抽象基底クラスを定義するためのもの (メタクラスとして実装されている)
    • 抽象メソッドを示すデコレータをメソッドに付け加えることで、継承先にメソッド実装を強制させることができる
    • Javaのabstractクラスと似たような概念
  • Protocol (Python 3.8以降)
    • 静的タイプチェッカーがダックタイピングを認識するためのクラスを定義できる
    • typingに実装されている型ヒント機能の一部
    • Golangのinterfaceと似たような概念 (異なるところもあります)

それぞれの使い勝手をおさらい

ABC

abc.ABC を継承することで抽象クラスを作ることができます。(ちなみに、ABCはメタクラスとして ABCMeta を指定してくれるヘルパークラスです。)

@abstractmethod がついたメソッドは抽象メソッドとなり、継承先クラスでの実装が強制されます。そうでないメソッドは非抽象メソッドとなり、継承先クラスでの実装は強制されませんし、実装しない場合はこの抽象メソッドの実装が実行されます。

ここからは、実装クラスと抽象型を引数のとる関数、そしてその関数の呼び出し時に実装クラスのインスタンスを渡す部分をおさらいします。

ちなみに、実装クラスが抽象メソッドの実装を満たしていない場合は、インスタンス生成時にランタイムエラーが発生します。試しに、findメソッドの実装を削除してみます。

Protocol

typing.Protocol を継承することで継承することでプロトコルを作ることができます。

プロトコルを使った場合でも、ABCの非抽象メソッドのようにデフォルト実装を持たせることができます。ただし、このデフォルト実装が実行されるのはこのプロトコルを継承した場合のみであり、そうでなければ実装クラス自身で実装する必要があります。

以下は、継承せずにプロトコルを実装した例です。

先ほどと同じように、findメソッドの実装をやめてみると、mypyやpylanceといった静的タイプチェッカーがエラーを出力します。

もっとも、Protocolはタイプヒント機能の一種であるため、実際に実行してみるとインスタンス生成自体はできますが、その後のfindメソッドの呼び出し時に AttributeError: 'DBUserRepository' object has no attribute 'find' というランタイムエラーが発生します。

このように、継承をすることなくプロトコルを実装することができ、タックタイピングの嬉しい点である、継承による複雑化を避けることができます。

実装クラスからの継承

プロトコルといっても、結局はクラスなので継承することもできます。以下が継承してみた実装クラスです。

継承すると、以下のようなできるようになります。

  • プロトコルのデフォルト実装を使用することができるようになる (ABCのような使い方)
  • IDEのジャンプ機能が使えるようになる (プロトコルの各メソッド↔︎実装クラスの各メソッド)

個人的には2つ目が嬉しい点で、継承しなければ、IDEのジャンプ機能でプロトコルの実装クラスを追うことは不可能です。そのため、プロトコルを使う場合でも、継承した方が実際のところは作業しやすいのではないかと思います。

どっちを使うか?

基本的には、冒頭に書いたように以下のような使い分けになると思います。

  • クラスの抽象化をしたいのであればABC
  • ダックタイピングをしたいのであればProtocol

その上で、基本的にはProtocolを使った方が良いと思いました。その理由は、以下のとおりです。

  • 継承が不要であるため、クラスの階層構造が生まれない
  • その上で、必要であれば、継承することでABCの非抽象メソッドのようなことをすることもできる

こういった実装がPythonになされている背景には、クラスを持たないプログラミング言語の台頭があると思います。継承を好まない人にもPythonを気持ちよく書けるように、ダックタイピングが取り入れられたのではないかと考えます。
実際、ProtocolのPEPでは、TypeScriptやGolangが引き合いに出されています。

さいごに

Pythonは歴史が長いため、目的を達成するための手段が複数用意されていることがあります。その中でどれが最適なのかを調べると奥が深くて面白いため、今後も記事にしていきたいと思います。

おすすめ書籍

エキスパートPythonプログラミング 改訂4版 (アスキードワンゴ) Effective Python 第2版 ―Pythonプログラムを改良する90項目 動かして学ぶ!Python FastAPI開発入門

blog-page_footer_336




blog-page_footer_336




-BackEnd

執筆者:

免責事項

このブログは、記事上部に記載のある投稿日時点の一般的な情報を提供するものであり、投資等の勧誘・法的・税務上の助言を提供するものではありません。仮想通貨の投資・損益計算は複雑であり、個々の取引状況や法律の変更によって異なる可能性があります。ブログに記載された情報は参考程度のものであり、特定の状況に基づいた行動の決定には専門家の助言を求めることをお勧めします。当ブログの情報に基づいた行動に関連して生じた損失やリスクについて、筆者は責任を負いかねます。最新の法律や税務情報を確認し、必要に応じて専門家に相談することをお勧めします。


comment

メールアドレスが公開されることはありません。 が付いている欄は必須項目です

CAPTCHA


関連記事

Go言語

Goのクエリビルダー goqu を使ってみる

1 はじめに2 goquとは2.1 対応するDB3 基本的な使い方3.1 Insert句を生成する3.2 Select句を生成する3.3 Update句を生成する3.4 Delete句を生成する4 よ ...

Stripe Connectを使ってCheckoutを利用した継続課金を実装

1 はじめに1.1 Checkoutを使う場合の動線2 決済画面への遷移2.1 マイグレーション2.2 Checkout Sessionの作成3 決済完了後の制御4 おまけ4.1 ローカルでWebho ...

laravel logo

[Laravel] middlewareでHTTPリクエストの前後に処理を入れる

1 はじめに2 middlewareとは3 実装3.1 middleware作成3.2 コード実装4 middleware定義4.1 常に呼び出す4.2 特定のURLにアクセスした時のみ呼び出す4.3 ...

Go言語

Goの抽象構文木でコードを解析する

1 はじめに1.1 抽象構文木とは2 ASTでコードを解析する2.1 サンプルコードを解析する2.2 構造体の木構造を確認する2.3 メソッドの木構造を確認する3 任意の対象を捜索する4 ASTをファ ...

Go言語

Go 1.16でのgo getとgo installの変更点

1 はじめに2 go getとgo installの役割2.1 go getの役割2.2 go installの役割3 この変更で何が改善されたのか3.1 go:generateと併用する4 その他の ...

フォロー

blog-page_side_responsive

2024年7月
 123456
78910111213
14151617181920
21222324252627
28293031  

アプリ情報

私たちは無料アプリもリリースしています、ぜひご覧ください。 下記のアイコンから無料でダウンロードできます。