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


関連記事

RubyのHanamiチュートリアルをやってみた

1 はじめに2 紹介2.1 Hanamiとは?2.2 Hanamiを選ぶ理由2.2.1 軽量2.2.2 アーキテクチャとして2.2.3 スレッドセーフ3 チュートリアルのための準備4 実践4.1 はじ ...

Rust入門してみた その5 ライフタイム

1 はじめに2 ライフタイムと借用チェッカー3 コンパイラにライフタイムを教える3.1 ライフタイムの指定が不要なケース4 ライフタイムの省略5 さいごに6 おすすめ書籍 はじめに Rustには、借用 ...

rails

Ruby2.4でCookieを手動で復号する際に発生したエラーの対処

1 はじめに1.1 前提条件2 発生したエラー2.1 実際のコード2.2 エラー詳細2.3 原因3 どう対処したか3.1 修正後のコード はじめに こんにちは、onoです。 現在開発中のアプリケーショ ...

rails

Rails5で出力される「alias_method_chain is deprecated.」について

1 はじめに2 alias_method_chainとは3 Module#prependとは4 とりあえず、DEPRECATION WARNINGの出力をなくしたい5 さいごに はじめに 直近のプロジ ...

markdownで書けるドキュメントツールのGitbookを試す

1 はじめに2 Gitbookとは3 nvm4 node.jsインストール5 Gitbook導入5.1 インストール5.2 初期化5.3 ローカルでブラウザから確認6 作成と編集6.1 見出し編集7 ...

フォロー

blog-page_side_responsive

2024年7月
 123456
78910111213
14151617181920
21222324252627
28293031  

アプリ情報

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