到達点は、型注釈を実行時検査と混同せず、関数の契約を示すことです。前提は第05・08回です。
型は読み手と検査道具への情報
引数と戻り値へ型を付けると、関数が何を受け取り何を返すかが分かります。mypyなどの静的型チェッカーは実行前に矛盾を探します。一方、JSONやユーザー入力の正しさは、実行時に別途検査する必要があります。
from typing import Protocol
class Sender(Protocol):
def send(self, message: str) -> None: ...
class Recorder:
def __init__(self) -> None:
self.messages: list[str] = []
def send(self, message: str) -> None:
self.messages.append(message)
def complete(title: str, sender: Sender) -> None:
sender.send(f"完了: {title}")
def find_title(titles: dict[int, str], task_id: int) -> str | None:
return titles.get(task_id)
recorder = Recorder()
complete("型を学ぶ", recorder)
print(recorder.messages)
title = find_title({1: "復習"}, 2)
print("見つかりません" if title is None else title)
main.pyとして実行すると、一件の記録と「見つかりません」が出ます。RecorderはSenderを継承していませんが、必要なsendを持つため構造的に適合します。省略記号はProtocol内の操作の宣言で、業務処理の未実装を隠すためのものではありません。
型を絞ると分岐が見える
str | None は文字列か不在のどちらかです。Noneでないと確認した後なら文字列として扱えます。Anyを多用すると検査を弱めてしまいます。何でも受け取るのではなく、境界で検証した型へ変換して内部へ渡します。
外部入力が辞書なら、TypedDictで期待構造を記述できますが、それだけでは値を実行時に検証しません。型チェッカーの設定と版も開発環境へ記録します。型チェックが通ることと、業務ルールが正しいことは別なので、テストと併用します。
練習と解答
練習:find_titleの結果へ、確認せずupperを呼ぶコードを書き、静的検査で指摘される理由を説明します。
解答:戻り値にはNoneが含まれ、Noneにupperはありません。Noneの分岐を設けるか、見つからなければ例外にする別の契約へ変えます。単にcastで警告を消しても実行時の安全は増えません。
公式資料
Python全20回の目次 | 前の回 | 次の回