こんにちは、かつコーチです。
チームで開発しているコードに、def greet(name: str) -> str:のような記述を見かけて、「これは何のためにあるのか」と気になったことはないでしょうか。
「型ヒントを書いても、実行結果は変わらないのでは」
「Optionalやlist[int]の書き方が分からない」
そう感じている方も多いと思います。
この記事では、型ヒントの基本構文、Optional・Union・list[int]などの書き方、そしてmypyとの組み合わせ方までを解説します。
読み終える頃には、型ヒント付きのコードを読み書きできるようになります。
型ヒントの基本構文
型ヒントとは何か
型ヒントとは、変数や関数の引数・返り値に、想定している型を注釈として書く仕組みです。
Pythonは元々、変数の型を明示しなくても動く動的型付け言語(実行時に型が決まる言語)です。
型ヒントを書いても、Python自体の実行結果が変わることはありません。
あくまで人間や、後述するmypyのようなツールに向けた「注釈」という位置づけです。
def greet(name: str) -> str:
return f"こんにちは、{name}さん"
message = greet("かつコーチ")
print(message)
実行結果は次の通りです。
こんにちは、かつコーチさん
name: strは「nameは文字列を想定している」、-> strは「返り値は文字列を想定している」という意味です。
変数への型ヒント
関数の引数・返り値だけでなく、変数にも型ヒントをつけられます。
age: int = 30
price: float = 1980.0
is_active: bool = True
print(age, price, is_active)
実行結果は次の通りです。
30 1980.0 True
型ヒントをつけていても、実際には違う型の値を代入することが、Python自体としては可能です。
age: int = 30
age = "三十歳" # 型ヒントと違う型を代入してもエラーにはならない
print(age)
実行結果は次の通りです。
三十歳
このように、型ヒントだけでは間違いを実行時に防げません。
チェックには後述するmypyのようなツールが必要になります。
Optional・Union・list[int]等の書き方
list[int]で要素の型を指定する
リストや辞書など、要素を複数持つデータ構造については、[]を使って要素の型まで指定できます。
def total_price(prices: list[int]) -> int:
return sum(prices)
result = total_price([100, 200, 300])
print(result)
実行結果は次の通りです。
600
list[int]は「intの要素が並んだリスト」という意味です。
辞書であればdict[str, int]のように、キーと値それぞれの型を指定します。
def show_scores(scores: dict[str, int]) -> None:
for name, score in scores.items():
print(f"{name}: {score}点")
show_scores({"田中": 80, "佐藤": 95})
実行結果は次の通りです。
田中: 80点
佐藤: 95点
返り値がない関数には、-> Noneを指定するのが慣例です。
Optionalで「Noneの可能性がある」ことを示す
Optionalは、「その値がNoneかもしれない」ことを示す型ヒントです。
標準ライブラリのtypingモジュールからインポートして使います。
from typing import Optional
def find_user(user_id: int) -> Optional[str]:
users = {1: "田中", 2: "佐藤"}
return users.get(user_id)
print(find_user(1))
print(find_user(99))
実行結果は次の通りです。
田中
None
Optional[str]は、「str型か、またはNoneのどちらか」を意味します。
Python 3.10以降では、Optional[str]の代わりにstr | Noneという書き方も使えます。
def find_user(user_id: int) -> str | None:
users = {1: "田中", 2: "佐藤"}
return users.get(user_id)
どちらも意味は同じですが、Python 3.10以降のプロジェクトでは、str | Noneの方が簡潔で好まれる傾向にあります。
Unionで複数の型を許容する
Unionは、「複数の型のうちいずれか」を示す型ヒントです。
from typing import Union
def format_id(value: Union[int, str]) -> str:
return f"ID-{value}"
print(format_id(123))
print(format_id("abc"))
実行結果は次の通りです。
ID-123
ID-abc
Union[int, str]は、「intまたはstrのどちらか」を意味します。
こちらもPython 3.10以降では、int | strという書き方に置き換えられます。
def format_id(value: int | str) -> str:
return f"ID-{value}"
mypyへの軽い言及
mypyで型の矛盾をチェックする
mypyは、型ヒントをもとに、コードの中に型の矛盾がないかをチェックしてくれる静的型チェッカー(実行せずにコードを解析するツール)です。
インストールは次のコマンドで行います。
pip install mypy
型ヒントと矛盾するコードを用意し、mypyでチェックしてみます。
# sample.py
def add(a: int, b: int) -> int:
return a + b
result = add(1, "2")
print(result)
mypy sample.py
実行結果は次のようになります。
sample.py:5: error: Argument 2 to "add" has incompatible type "str"; expected "int" [arg-type]
Found 1 error in 1 file (checked 1 source file)
add関数はintを2つ受け取る想定なのに、"2"という文字列を渡してしまっている矛盾を、実行前に検出できています。
このように、型ヒントを書いておくと、mypyと組み合わせることで、実行前にバグの芽を見つけやすくなります。
チーム開発でCI(継続的インテグレーション)にmypyを組み込んでおくと、型の不整合を機械的に防げるようになります。
よくあるつまずきポイント・エラー対処
引数の初期値にNoneを使うと矛盾が起きやすい
筆者が型ヒントを書き始めたばかりの頃、デフォルト引数にNoneを使いつつ、型ヒントをlist[int]のままにしてしまい、mypyで警告を受けた経験があります。
❌ Before(Noneを許容していない型ヒント)
def add_item(item: int, items: list[int] = None) -> list[int]:
if items is None:
items = []
items.append(item)
return items
print(add_item(1))
mypyでチェックすると、次のようなエラーメッセージが表示されます。
sample.py:1: error: Incompatible default for argument "items" (default has type "None", argument has type "list[int]") [assignment]
型ヒント上はitemsがlist[int]のはずなのに、デフォルト値としてNoneを渡しているという矛盾が指摘されています。
Pythonの実行自体は問題なく通ってしまうため、mypyを使わないと気づきにくいミスです。
✅ After(Optionalで矛盾を解消した書き方)
from typing import Optional
def add_item(item: int, items: Optional[list[int]] = None) -> list[int]:
if items is None:
items = []
items.append(item)
return items
print(add_item(1))
実行結果は次の通りです。
[1]
itemsの型ヒントをOptional[list[int]](list[int] | Noneでも可)に変更したことで、「Noneが入りうる」ことが正しく表現され、mypyのエラーも解消されます。
「デフォルト値にNoneを使う引数は、型ヒントもOptionalにする」という組み合わせは、頻出パターンとして覚えておくと役立ちます。
まとめ
この記事のポイント
- 型ヒントは変数・引数・返り値に想定する型を注釈として書く仕組みで、実行結果自体は変えない
list[int]やdict[str, int]のように、要素の型まで指定できるOptional[型]は「その型かNone」、Union[型, 型]は「複数の型のいずれか」を示す- Python 3.10以降では
型 | None・型 | 型という書き方も使える mypyを使うと、型ヒントをもとにした矛盾を実行前に検出できる
次に読むべき記事
- 関数の定義方法の基本
- クラスとオブジェクト指向の基本
- コンテキストマネージャ(with文)の仕組みと自作方法
タグ: Python, 中級者向け, 基本文法