はじめに
Pythonは、変数や関数の引数に型を書かなくても動く言語です。例えば、次のような関数は普通に書けます。
def calculate_total(items):
return sum(items)
ただ、この関数をあとから読む人には、items に何を渡せばよいのかがすぐには分かりません。
- 整数のリストなのか
- 小数のリストなのか
- 文字列のリストでもよいのか
- 戻り値は
intなのかfloatなのか
小さなコードなら問題になりにくいですが、関数が増えてくると、この「読めばたぶん分かる」が少しずつ負担になります。そこで使えるのが 型ヒント(型アノテーション) です。
この記事では、Python 3.10以降を前提に、str | None や list[int] のような書き方を使います。古いPythonでは、typing.Optional や typing.List を使う書き方が必要になる場合があります。
型ヒントを書くと、同じ関数を次のように表せます。
def calculate_total(items: list[int]) -> int:
return sum(items)
これだけで、items は整数のリストで、戻り値は整数だと分かります。型ヒントは、Pythonの実行速度を速くするためのものではありません。主な目的は、コードを読む人と、エディタや静的解析ツールに意図を伝えることです。
型ヒントで何が嬉しいのか
型ヒントの良さは、実行する前に「このコードは少し怪しい」と気づきやすくなることです。例えば、先ほどの関数に文字列のリストを渡してしまったとします。
def calculate_total(items: list[int]) -> int:
return sum(items)
result = calculate_total(["100", "200", "300"])
このコードは、list[int] と書いているのに、実際には list[str] を渡しています。Python自体は、型ヒントだけを見て実行前に止めてくれるわけではありません。しかし、mypyやPyrightのような静的解析ツール、あるいはVS Codeなどのエディタは、この不一致を警告できます。つまり型ヒントは、バグをゼロにする魔法ではなく、間違いに早く気づくための目印です。
関数の引数と戻り値に型を書く
最もよく使うのは、関数の引数と戻り値への型ヒントです。
def greet(name: str) -> str:
return f"こんにちは、{name}さん"
この例では、name は str、戻り値も str です。書き方は次のようになります。
def 関数名(引数名: 引数の型) -> 戻り値の型:
...
複数の引数がある場合も同じです。
def add_tax(price: int, tax_rate: float) -> int:
return int(price * (1 + tax_rate))
price は整数、tax_rate は小数、戻り値は整数だと分かります。このように型を書いておくと、関数を使う側も迷いにくくなります。
listやdictの型を書く
リストや辞書も型ヒントで表せます。
numbers: list[int] = [1, 2, 3]
names: list[str] = ["Alice", "Bob", "Charlie"]
scores: dict[str, int] = {"Alice": 90, "Bob": 85}
list[int] は「整数のリスト」、dict[str, int] は「キーが文字列で、値が整数の辞書」という意味です。関数の引数にも使えます。
def average(scores: list[int]) -> float:
return sum(scores) / len(scores)
この関数は、整数のリストを受け取り、平均値を小数として返します。
Noneを返す可能性がある場合
Pythonでは、値が見つからなかったときに None を返す関数を書くことがあります。例えば、ユーザーIDから名前を取得する関数を考えます。
def get_user_name(user_id: int) -> str | None:
if user_id < 0:
return None
return f"User{user_id}"
str | None は、「文字列かもしれないし、Noneかもしれない」という意味です。このように書いておくと、使う側は None の可能性を意識できます。
name = get_user_name(-1)
if name is not None:
print(name.upper())
もし None の可能性を考えずに次のように書くと、実行時にエラーになる可能性があります。
name = get_user_name(-1)
print(name.upper())
型ヒントは、こうした「たまにだけ起きるエラー」を見つけやすくしてくれます。なお、古い書き方では Optional[str] もよく使われます。
from typing import Optional
def get_user_name(user_id: int) -> Optional[str]:
...
Optional[str] は str | None と同じ意味です。最近のPythonでは、str | None の方が読みやすい場面が多いです。
変数にも型ヒントを書ける
型ヒントは、関数だけでなく変数にも書けます。
name: str = "Python"
age: int = 25
height: float = 175.5
is_student: bool = True
ただし、単純な代入ではPythonやエディタが型を推測できることも多いです。
name = "Python"
age = 25
このような場合、毎回すべての変数に型を書く必要はありません。変数の型ヒントが特に役立つのは、空のリストや辞書を作るときです。
users: list[str] = []
scores: dict[str, int] = {}
空のリストだけを見ると、何を入れる予定なのか分かりません。型ヒントを書いておくと、あとから読んだときに意図が分かりやすくなります。
型ヒントは実行時に強制されない
ここは大事です。Pythonの型ヒントは、基本的には実行時に型を強制しません。例えば、次のコードは型ヒントとしては間違っています。
def double(x: int) -> int:
return x * 2
print(double("A"))
x: int と書いているので、本来は整数を渡す想定です。しかしPythonは、実行時にこの型ヒントを見て自動で止めるわけではありません。上のコードは、文字列 "A" を2倍して "AA" を出力します。つまり、型ヒントを書いただけで安全になるわけではありません。実際にチェックしたい場合は、mypyやPyrightなどの静的解析ツールを使います。
pip install mypy
mypy example.py
型ヒントは、Python本体というよりも、エディタ、レビュー、静的解析ツールと組み合わせて効く仕組みだと考えると分かりやすいです。
どこまで型を書くべきか
初心者のうちは、全部に型を書こうとしなくて大丈夫です。まずは、次の3つから始めるのが現実的です。
- 関数の引数
- 関数の戻り値
- 空のリストや辞書
特に、関数の入り口と出口に型を書くと、コード全体の見通しがかなりよくなります。逆に、関数の中の一時変数まで細かく書きすぎると、かえって読みにくくなることもあります。型ヒントは「多ければ多いほどよい」というより、読む人が迷いやすい場所に置くのが大事です。
まとめ
Pythonの型ヒントは、コードの意図を明確にするための仕組みです。特に、次のような場面で役立ちます。
- 関数の引数や戻り値を分かりやすくする
Noneが返る可能性を明示する- リストや辞書に何を入れる予定かを示す
- エディタの補完や警告を効かせやすくする
- mypyやPyrightで実行前にミスを見つける
型ヒントは、Pythonを静的型付け言語に変えるものではありません。しかし、コードが少し大きくなってきたとき、自分と未来の自分に向けたメモとしてかなり効いてきます。まずは関数の引数と戻り値から、少しずつ書いていくのがおすすめです。
さらに学びたい方へ
LangChainなどのLLMアプリケーションを書くときも、関数の引数や戻り値に型ヒントがあると、コードの見通しがよくなります。
LangChainとOpenAI APIでLLMプログラミングに入門する https://tobarilabo.com/langchain_starter/
独学でも十分進められますが、Pythonの基礎から動画で体系的に学びたい場合は、以下の講座も参考になります。


コメント