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 1.23 で追加された iter パッケージを使ってみる

1 はじめに1.1 iteratorとは2 push方式のイテレータ2.1 kとvを受け取るイテレータ関数2.2 イテレータ関数をチェインさせる2.3 イテレータ関数の型を利用する2.4 イテレーショ ...

rails

Ruby、Railsの時間に関するメソッドを使用してみた

はじめに 普段PHPのお仕事をしています、tonnyです。 半年程前からRuby on Railsの勉強を始めました。 今回はよく使う時間に関するメソッドついてまとめたいと思います。 目次 1 はじめ ...

rails

Railsでの非同期処理とDelayed Job

1 はじめに2 Active Job2.1 Active Jobの役割2.2 ジョブを作成する2.3 ジョブをキューに登録する2.4 コールバック2.5 例外3 Delayed Job3.1 設定3. ...

laravel logo

Deployerで複数の環境やサーバにデプロイするには

1 はじめに2 hosts.ymlファイルの作成3 プロダクション・ステージング環境を分ける3.1 ymlファイルの記載3.2 デプロイコマンド4 複数台のサーバに同時リリースする4.1 ymlファイ ...

Go言語

Go言語で使えるmigrationライブラリ

1 はじめに2 migrationライブラリ2.1 goose2.2 sql-migrate2.3 migu2.4 pop2.5 sqldef3 sqldefを使ってみる3.1 導入3.2 テーブルを ...

フォロー

blog-page_side_responsive

2024年7月
 123456
78910111213
14151617181920
21222324252627
28293031  

アプリ情報

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