Python型ヒント完全ガイド:typingとmypyで静的解析・バグ防止・可読性向上
Pythonの型ヒント(型アノテーション)の基本からtypingモジュールの高度な型、mypyによる静的解析までを解説。コードのバグ防止と可読性向上に役立つ実践的なテクニックを紹介。
Python型ヒントとは?
Python 3.5以降で導入された型ヒント(Type Hints)は、変数や関数の引数・戻り値に期待される型を明示するための文法です。動的型付け言語であるPythonに静的型付けの利点をもたらし、コードの可読性向上やバグの早期発見に貢献します。
基本的な書き方
def greet(name: str) -> str:
return f"Hello, {name}"
name: str は引数nameがstr型であることを示し、-> str は戻り値がstr型であることを示します。これらのアノテーションは実行時には強制されず、あくまで開発者やツールへのヒントとして機能します。
typingモジュールの主要な型
typing モジュールを使うことで、より複雑な型を表現できます。
基本的な型エイリアス
List[int]:整数のリストDict[str, float]:キーがstr、値がfloatの辞書Tuple[int, str, float]:3要素のタプルSet[bytes]:バイト列のセットOptionalとUnion
from typing import Optional, Union
def find_user(user_id: int) -> Optional[dict]:
# ユーザーが見つからない場合はNoneを返す
...
def process(value: Union[int, str]) -> None:
print(value)
Optional[X] は Union[X, None] の省略形で、値がX型またはNoneであることを示します。
AnyとTypeVar
from typing import Any, TypeVar
T = TypeVar('T')
def first(items: list[T]) -> T:
return items[0]
Any は任意の型を許容し、TypeVar はジェネリックな型変数を定義します。
CallableとLiteral
from typing import Callable, Literal
def apply(func: Callable[[int, int], int], a: int, b: int) -> int:
return func(a, b)
def set_mode(mode: Literal['read', 'write']) -> None:
pass
Callable[[Arg1Type, Arg2Type], ReturnType] で関数の型を、Literal で特定のリテラル値のみを許容します。
mypyによる静的解析
mypyはPythonの静的型チェッカーです。型ヒントを解析し、型の不一致を検出します。
インストールと基本的な使い方
pip install mypy
mypy my_script.py
設定ファイル(mypy.ini)
[mypy]
python_version = 3.10
strict_optional = True
warn_unused_ignores = True
ignore_missing_imports = True
実践的なチェック例
<h1>sample.py</h1>
def add(a: int, b: int) -> int:
return a + b
result = add(1, "2") # mypyがエラーを報告
実行結果:
sample.py:4: error: Argument 2 to "add" has incompatible type "str"; expected "int"
Found 1 error in 1 file (checked 1 source file)
型ヒントのベストプラクティス
1. 公開APIには必ず型ヒントを付ける
ライブラリやモジュールの公開関数・クラスには型ヒントを付けることで、利用者が正しく使えるようになります。
2. 複雑な型はエイリアスを定義する
from typing import Dict, List, Tuple
UserData = Dict[str, Union[str, int]]
UserList = List[UserData]
3. プロトコルで構造的サブタイピング
from typing import Protocol
class Drawable(Protocol):
def draw(self) -> None: ...
def render(obj: Drawable) -> None:
obj.draw()
4. 過剰な型付けを避ける
全ての変数に型を書く必要はありません。複雑な内部処理では型推論に任せ、境界部分で明示するのが良いバランスです。
5. サードパーティライブラリの型スタブ
typeshed や types-* パッケージを活用し、外部ライブラリの型情報を補完します。
pip install types-requests types-PyYAML
型ヒントによるバグ防止の実例
ケース1: Noneチェックの漏れ
from typing import Optional
def get_name(user: Optional[dict]) -> str:
return user["name"] # mypyがエラー: userはNoneの可能性
修正:
def get_name(user: Optional[dict]) -> str:
if user is None:
return "Guest"
return user["name"]
ケース2: 引数の型誤り
def calculate_discount(price: float, rate: float) -> float:
return price * rate
<h1>誤った呼び出し</h1>
discount = calculate_discount(100, "0.1") # 実行時エラー
mypyが事前に型不一致を検出します。
高度な型ヒントの活用
ジェネリクスとTypeVar
from typing import TypeVar, Generic
T = TypeVar('T')
class Stack(Generic[T]):
def __init__(self) -> None:
self.items: list[T] = []
def push(self, item: T) -> None:
self.items.append(item)
def pop(self) -> T:
return self.items.pop()
オーバーロード
from typing import overload
@overload
def process(data: int) -> str: ...
@overload
def process(data: str) -> int: ...
def process(data):
if isinstance(data, int):
return str(data)
else:
return len(data)
型ガード
def is_str_list(val: list[object]) -> bool:
return all(isinstance(x, str) for x in val)
まとめ
Pythonの型ヒントは、コードの品質を高め、バグを減らし、チーム開発でのコミュニケーションを円滑にする強力なツールです。typingモジュールとmypyを組み合わせることで、動的言語の柔軟性を保ちながら静的型付けの恩恵を受けられます。
まずは簡単な関数から型ヒントを導入し、徐々にプロジェクト全体に拡大することをおすすめします。特に公開APIや複雑なロジックには積極的に型を付け、mypyのチェックをCIに組み込むと効果的です。
型ヒントは実行時パフォーマンスに影響を与えず、あくまで開発時の補助ツールです。Pythonの「書きやすさ」を損なうことなく、「読みやすさ」と「信頼性」を向上させるために、ぜひ活用してみてください。