型ヒント(タイプヒント、type hints)は、変数や引数、戻り値が「どんな型か」をコードに書き添える機能です。関数の引数にname: str、戻り値に-> strのように注釈を付けると、コードの意図が明確になり、エディタの補完やエラー検出も効くようになります。大規模な開発やチーム開発で、バグを未然に防ぐのに役立ちます。
最大の注意点は、型ヒントは実行時には強制されないことです。name: strと書いても、Pythonは実際に文字列が渡されたかをチェックしません。あくまで「ヒント(目印)」であり、型のチェックはmypyなどの別ツールで行います。この性質を知らないと「型を書いたのにエラーにならない」と戸惑います。この記事では、実機のPython 3.12で確認しながら、型ヒントを整理します。
- 引数は
name: str、戻り値は-> str、変数はage: int = 30と書きます。 - 型ヒントは実行時に強制されません。違う型を渡してもそのまま動きます。
- リストや辞書は
list[int]・dict[str, int](Python 3.9以降)と書けます。 - Noneを許すときは
str | None(Python 3.10以降)またはOptional[str]です。 - 型が正しいかはmypyなどのツールで静的にチェックします。
- 注釈は
__annotations__で確認でき、エディタの補完にも使われます。
型ヒントを付ける対象の関数(def)、型そのものの扱いは型変換、list[int]などで使うリストもあわせて参考になります。
型ヒントとは(基本の書き方)
関数では、引数名のあとに: 型、引数リストのあとに-> 型で戻り値の型を書きます。デフォルト値と組み合わせるときはage: int = 0のようにします。
# name は str、age は int(デフォルト0)、戻り値は str
def greet(name: str, age: int = 0) -> str:
return f"{name}({age}歳)"
print(greet("田中", 30)) # 田中(30歳)
# 引数: int、戻り値: int
def add(a: int, b: int) -> int:
return a + b
実機でも、greet("田中", 30)は田中(30歳)を返しました。name: strは「nameは文字列のつもり」、-> strは「戻り値は文字列のつもり」という意味です。型ヒントを付けても動作は変わりませんが、コードを読む人やエディタにとって「ここには何が入るか」が明確になります。とくに引数が多い関数では、型ヒントがあると使い方がぐっと分かりやすくなります。
【最重要】実行時には強制されない
もっとも重要な性質がこれです。型ヒントと違う型の値を渡しても、Pythonはエラーにせず、そのまま実行します。型ヒントは「こうあるべき」という目印にすぎず、実行時のチェックは行われません。
def greet(name: str, age: int = 0) -> str:
return f"{name}({age}歳)"
# name に数値、age に文字列を渡しても…
print(greet(123, "文字列"))
# → エラーにならず「123(文字列歳)」と動いてしまう
# Python は型ヒントを「チェック」しない。あくまでヒント
実機で確認したところ、name: str・age: intと書いた関数に、数値と文字列を逆に渡しても(greet(123, "文字列"))エラーにならず、123(文字列歳)とそのまま動いてしまいました。これは、Pythonが型ヒントを実行時にチェックしないためです。型ヒントは、あくまでコードを読む人やツールのための「目印」であり、間違った型を渡しても止めてはくれません。「型を書いたのに、なぜか型違反でエラーにならない」と感じるのは、この仕様によるものです。実際に型の誤りを検出したいときは、次に紹介するmypyなどの静的解析ツールを使います。型ヒントは「ドキュメント+ツール連携」のためのもの、と理解しておくと混乱しません。
変数・リスト・辞書の型ヒント
変数にもage: int = 30のように型ヒントを付けられます。リストや辞書の中身の型は、list[int]・dict[str, int]のように書きます(Python 3.9以降)。
# 変数の型ヒント
age: int = 30
name: str = "佐藤"
# リストの中身が int(Python 3.9 以降)
def total(nums: list[int]) -> int:
return sum(nums)
print(total([1, 2, 3])) # 6
# 辞書: キーが str、値が int
scores: dict[str, int] = {"国語": 80, "数学": 95}
実機でも、list[int]と注釈した関数に[1, 2, 3]を渡して6が得られました。list[int]は「intを要素に持つリスト」、dict[str, int]は「キーがstr、値がintの辞書」という意味です。Python 3.9より前のバージョンでは、from typing import List, DictとしてList[int]・Dict[str, int]と書く必要があります。新しいPythonでは、組み込みのlist・dictをそのまま使えて簡潔です。
Optional(Noneを許す)とUnion
「文字列またはNone」のように複数の型を許す場合は、str | Noneと書きます(Python 3.10以降)。これはtypingモジュールのOptional[str]やUnion[str, None]と同じ意味です。
# str または None(Python 3.10 以降の書き方)
def find(key: str) -> str | None:
return None # 見つからなければ None を返す、など
# typing モジュールを使う書き方(古いバージョンでも動く)
from typing import Optional, Union
def find2(key: str) -> Optional[str]: # str | None と同じ
return None
def f(x: Union[int, str]) -> int: # int | str と同じ
return 0
実機でも、str | NoneやOptional[int]・Union[int, str]の型注釈が問題なく使えました。Optional[str]は「strまたはNone」という意味で、「見つからなければNoneを返す」ような関数でよく使います。Union[int, str]は「intまたはstr」です。Python 3.10以降では、str | None・int | strのように|(パイプ)で書くほうが簡潔で読みやすく、こちらが推奨されています。古いバージョンを対象にする場合はtypingのOptional・Unionを使います。
mypyで静的にチェックする
型ヒントを「実際に検査する」には、mypyなどの静的解析ツールを使います。プログラムを実行せずに、型ヒントと矛盾する箇所を事前に指摘してくれます。
# インストール # pip install mypy # チェックの実行 # mypy script.py # 例: greet(123, "文字列") のような型違反があると、 # mypy は「str を期待しているのに int が渡された」と警告する # → 実行する前にバグに気づける
mypyは、pip install mypyでインストールし、mypy ファイル名で実行します。プログラムを動かす前に、型ヒントと食い違う箇所を検出してくれます。Pythonそのものは型ヒントをチェックしませんが、mypyを組み合わせることで、「型ヒントを書く意味」が生きてきます。VS Codeなどのエディタも、型ヒントをもとにリアルタイムで警告を出してくれます。型ヒントは、こうしたツールと組み合わせて初めて真価を発揮します。
__annotations__・なぜ書くのか
付けた型ヒントは__annotations__という属性で確認できます。型ヒントは実行を変えませんが、コードの可読性・エディタの補完・バグの早期発見という大きなメリットがあります。
def greet(name: str, age: int = 0) -> str:
return f"{name}({age}歳)"
print(greet.__annotations__)
# {'name': <class 'str'>, 'age': <class 'int'>, 'return': <class 'str'>}
# 型ヒントはここに記録されている(実行には影響しない)
実機でも、greet.__annotations__で{'name': str, 'age': int, 'return': str}と、付けた型ヒントが記録されているのを確認できました。型ヒントを書くメリットは、①コードを読む人に意図が伝わる、②エディタが補完や警告を出してくれる、③mypyでバグを事前に発見できるの3つです。小さなスクリプトでは省略しても問題ありませんが、少し規模が大きくなったり、他の人と共有したりするコードでは、型ヒントを付けると保守がぐっと楽になります。
主な書き方まとめ
型ヒントの主な書き方をまとめます。
| 対象 | 書き方 |
|---|---|
| 引数 | def f(name: str, age: int = 0): |
| 戻り値 | def f(...) -> str: |
| 変数 | age: int = 30 |
| リスト / 辞書 | list[int] / dict[str, int] |
| Noneを許す | str | None(またはOptional[str]) |
| 複数の型 | int | str(またはUnion[int, str]) |
よくある失敗
型ヒントで実行時チェックされると思う
強制されません。違う型を渡しても動きます。検査はmypyで行います。
古いPythonでlist[int]を使う
3.9より前はfrom typing import ListとしてList[int]を使います。
str | NoneをPython 3.9以前で使う
|記法は3.10以降です。古いバージョンはOptional[str]を使います。
Optionalを「省略可能」の意味だと思う
Optional[str]は「strまたはNone」の意味です。引数の省略可否とは別です。
型ヒントを必須だと思う
型ヒントは任意です。付けなくても動きますが、付けると保守性が上がります。
よくある質問
name: strと書いても、数値を渡してエラーにはなりません。型ヒントはあくまで「目印」で、実際の検査はmypyなどの静的解析ツールやエディタが行います。list[int](intのリスト)、dict[str, int](キーがstr・値がintの辞書)のように書きます。これはPython 3.9以降の書き方です。3.9より前のバージョンではfrom typing import List, DictとしてList[int]・Dict[str, int]を使います。str | Noneと書きます(Python 3.10以降)。「文字列またはNone」という意味です。古いバージョンではfrom typing import OptionalとしてOptional[str]を使います。どちらも同じ意味です。Optional[str]は「strまたはNone」という型の意味で、引数を省略できるかどうか(デフォルト値の有無)とは別です。引数を省略可能にするにはdef f(x: str = ""):のようにデフォルト値を付けます。まとめ
- 引数は
name: str、戻り値は-> str、変数はage: int = 30。 - 型ヒントは実行時に強制されません。違う型を渡しても動きます。
- リスト・辞書は
list[int]・dict[str, int](3.9以降)。 - Noneを許すのは
str | None(3.10以降)またはOptional[str]。 - 型の検査はmypyなどのツールで行います。
型ヒントは、Pythonの動作を変えずに、コードの読みやすさと安全性を高める機能です。「実行時には強制されない(チェックはmypyなどで行う)」という性質さえ理解すれば、混乱なく使えます。まずは関数の引数と戻り値に型を書くところから始めて、エディタの補完が効く快適さを体験してみてください。
