採点されません。匿名です。 間違えることは学習の一部です。読むだけでもかまいません。

class ch03 — Protocol と ABC

テクノロジ系 / L07 class — クラスとデータ構造

class ch03 — Protocol と ABC

「型の約束」を 2 種類使い分ける

ゴール

  • typing.Protocol で「形が同じなら OK」(構造的部分型)を書ける
  • abc.ABC + @abstractmethod で「継承を強制」する書き方が読める
  • Protocol と ABC の使い分け基準を 1 行で言える

なぜ「型の約束」が要るのか

def send(notifier, message: str) -> None:
    notifier.notify(message)
  • notifier何でも 渡せてしまう (動的型付け)
  • notify(message) を持っている」と 保証 したい
  • → Protocol / ABC の出番

Protocol: 「形が合えば仲間」

from typing import Protocol

class Notifier(Protocol):
    def notify(self, message: str) -> None: ...

class SlackNotifier:
    def notify(self, message: str) -> None:
        print(f"[Slack] {message}")

def send(n: Notifier, msg: str) -> None:
    n.notify(msg)

send(SlackNotifier(), "hi")   # OK: 継承していなくても notify があれば OK

SlackNotifierNotifier を継承していない。それでも型チェックを通る。

ABC: 「継承して実装を強制」

from abc import ABC, abstractmethod

class Notifier(ABC):
    @abstractmethod
    def notify(self, message: str) -> None: ...

class SlackNotifier(Notifier):       # 継承が必須
    def notify(self, message: str) -> None:
        print(f"[Slack] {message}")

# Notifier()                    # ❌ TypeError: abstract メソッドあり
# 未実装で継承すると、インスタンス化時に落ちる

並べて見る

ProtocolABC
関係構造的 (duck typing)名義的 (継承必須)
既存クラスへの後付けできるできない (継承を直す必要あり)
実装漏れの検出mypy / pyright 等の 静的 チェック実行時TypeError
共通実装の共有しにくいしやすい (具象メソッドを持てる)

使い分け 1 行ルール

  • 外部から渡ってくる「形」だけ縛りたいProtocol
  • 自分で抽象クラスを設計し共通実装を持たせたいABC

FastAPI の DI (Depends) や差し替え可能な実装は、ほぼ Protocol で足りる。

drill

drill/Notifier Protocol と Slack / Mail 2 種類の実装を完成させる

よくある詰まり

症状原因対処
TypeError: Can't instantiate abstract classABC を継承したが @abstractmethod を実装していないサブクラスでメソッドを定義
Protocol が効いていない感じProtocol は実行時には基本ノーチェックmypy / pyright で静的検査する
isinstance(x, MyProtocol) で落ちる@runtime_checkable が無い@runtime_checkable を付ける

次に学ぶ領域

領域続けて読む
dataclass で値オブジェクトclass/ch02-dataclass
FastAPI の Depends で差し替えfastapi/ch05-depends
テスト時にダミー実装test/ch01-pytest
力試ししたい方は クイズ形式の演習(任意) もあります。読むだけでも十分です。