リストや辞書の型ヒント

Python で x: int = 10 のように単一の値に型を付けるのは簡単だが、リストや辞書のようなコレクションでは「中身の型」まで指定したくなる。Python 3.9 以降では組み込み型をそのままジェネリクスとして使えるようになり、記述がぐっとシンプルになった。

リストの型ヒント

リストに型ヒントを付けるには、角括弧で要素の型を指定する。

names: list[str] = ["Alice", "Bob", "Charlie"]
scores: list[int] = [85, 92, 78]

list[str] は「文字列のリスト」を意味する。こう書いておくと、mypy などの型チェッカが scores.append("hello") のような誤りを検出してくれる。

要素が混在するリストには Union を使う方法もあるが、基本的には1種類の型で統一するほうが扱いやすい。

mixed: list[int | str] = [1, "two", 3]

Python 3.10 以降では int | str のようにパイプで Union を表現できる。3.9 以前なら typing.Union[int, str] を使う。

辞書の型ヒント

辞書にはキーの型と値の型を指定する。

user: dict[str, int] = {"Alice": 30, "Bob": 25}
config: dict[str, str] = {"host": "localhost", "port": "8080"}

dict[str, int] は「キーが文字列、値が整数の辞書」という意味になる。値の型が混在する辞書を正確に表現したい場合は、後の記事で扱う TypedDict が便利だ。

タプルの型ヒント

タプルは要素ごとに型を指定できる点がリストと異なる。

point: tuple[float, float] = (3.14, 2.71)
record: tuple[str, int, bool] = ("Alice", 30, True)
リストの型ヒント

list[int] のように要素の型を1つだけ指定する。長さは不定。

タプルの型ヒント

tuple[str, int, bool] のように各位置の型を個別に指定する。長さが固定される。

可変長のタプルを表現したい場合は、省略記号を使う。

values: tuple[int, ...] = (1, 2, 3, 4, 5)

tuple[int, ...] は「任意の個数の整数からなるタプル」を表す。

セットの型ヒント

セットもリストと同じ要領で書ける。

tags: set[str] = {"python", "typing", "tutorial"}
ids: frozenset[int] = frozenset([1, 2, 3])

frozenset にも型ヒントを付けられる。変更不可なセットが必要な場面で使う。

ネストしたコレクション

コレクションの中にコレクションを入れる場合も、型を入れ子にすればよい。

matrix: list[list[int]] = [
    [1, 2, 3],
    [4, 5, 6],
]

users: dict[str, list[int]] = {
    "Alice": [85, 92, 78],
    "Bob": [90, 88, 95],
}

dict[str, list[int]] は「キーが文字列、値が整数リストの辞書」だ。ネストが深くなると読みにくくなるので、そういう場面では TypeAlias を使って名前を付けるとよい。

Python 3.8 以前の書き方

Python 3.9 で組み込み型がジェネリクスに対応する前は、typing モジュールからインポートする必要があった。

from typing import List, Dict, Tuple, Set

names: List[str] = ["Alice", "Bob"]
user: Dict[str, int] = {"Alice": 30}
point: Tuple[float, float] = (3.14, 2.71)
tags: Set[str] = {"python", "typing"}
3.9 以降

list[str], dict[str, int] のように組み込み型をそのまま使える。インポート不要。

3.8 以前

typing.List[str], typing.Dict[str, int] のように typing モジュールからインポートが必要。

現在は 3.9 以降の書き方が主流であり、新規コードでは typing.List などを使う理由はほとんどない。古いコードベースを読む際に知っておくと役立つ程度だ。