python
Pythonの様な動的型付けスクリプト言語は「CやJavaみたいにいちいち型を指定しなくてよいのが書きやすくていいよね」 などとおすすめされることも多いが、書き捨てのスクリプトを超えて大きいアプリなどを作り出すとやはり型の指定が欲しくなることも間々ある。
そんなこともあり、Pythonではかなり遅く3.5以降では型アノテーション(Type Annotation)という機能が追加された。 これは本来動的型付け言語であるPythonの変数や関数の定義に想定する型をメモしておくことで
- 人間がコードを読んだときに理解しやすくする
- mypyなどの静的解析ツールで型の不整合をチェックできる
- IDE(統合開発環境)で補完やリファクタリングを支援できる
- 最近ではAIコードアシスタント・コーディングエージェントのコード理解の正確性が上がる
ただやはり静的型付け言語の様な厳密なチェックが行われるわけではなく、デフォルトではただのメモで違反があってもエラーにはならない、 また言語仕様に後付けされたため書き方にやや癖がある、バージョンによって変わる点があるなど一筋縄ではいかないのでメモしておく。
この記事ではPython3.10以降での型アノテーションの書き方を説明する。それ以前ではintやstrのような型名を直接アノテーションに使えず、 専用のオブジェクトをtypingモジュールをインポートする必要があった。
変数の型アノテーション
変数名の後にコロン(:)と型名を書く。値を代入するときはそのあとにイコールを書く。number: int = 5 # numberはint型、初期値5 float_number: float = 1.02 # float型 name: str = "hello" # str型 flag: bool = True # bool型 items: list = [1, 2, 3] # list型、中身は何でもよいなおバリデーションなどを別に行っていない限り、あとから適合しないデータを入れたり別のアノテーションで上書きしてもエラーにはならない。 なおインタープリタがエラーにしないからと言って、人間の気持ち上問題があるのでやめたほうが良い。
number: int = 5 # numberはint型、初期値5 number = "hello" # これはエラーにならない number: float = 1.02 # これもエラーにならない
関数の型アノテーション
仮引数と戻り値に型アノテーションをつける。
def func(name: str, number: int) -> bool:
print(f"name={name}, number={number}")
return True
複合データ型のアノテーション
リストや辞書などの複合データ型のアノテーションには中身の型も指定できる。しない場合は中身は何でもよいという扱いになる。「intかstrのリスト」のよいに複数nの型を指定することも、
str | intのようにできる。from typing import Any lst: list[int] = [1, 2, 3] # int型のリスト str_lst: list[str] = ["a", "b", "c"] # str型のリスト any_lst: list = [1, "a", 2.0] # 中身は何でもよい any_lst2: list[Any] = [1, "a", 2.0] # Anyを明示してもよい or_lst: list[int | str] = [1, "a", 2, "b"] # int型またはstr型のリスト
dct: dict[str, int] = {"a": 1, "b": 2} # keyがstr型、valueがint型の辞書
or_dct: dict[str, int | str] = {"a": 1, "b": "hello"} # valueがint型またはstr型の辞書
tpl: tuple[int, str, float] = (1, "a", 2.0) # int, str, floatの順の長さ3のタプル int_tpl: tuple[int, ...] = (1, 2, 3, 4) # 全ての要素がint型、長さ不定のタプル
Typingオブジェクト
型情報を表記するためのオブジェクトがtypingモジュールに用意されている。Python3.10以降は柔軟な型アノテーションができるので必ずしもこれを使う必要はないが、使わないと書けないパターンがあるため、 また後方互換性のために残されている。
- Any: 型が何でもよいことを意味する
- Union: 複数の型のいずれか(Python 3.10以降では型1 | 型2のように書ける)
- Optional: Noneを含む型(Python 3.10以降では型 | Noneのように書ける)
from typing import Any, Union, Optional
value1: Any = 5 # 型は何でもよい
value2: Union[int, str] = "hello" # int型またはstr型
value3: Optional[int] = None # int型またはNone
def func(name: str, number: Optional[int] = None) -> None: # numberはint型またはNone、デフォルト値はNone
print(f"name={name}, number={number}")
型チェック:アノテーションへの違反を検知してエラーに
せっかく型アノテーションしたからには間違っていたら教えてほしいのだが、Pythonインタープリタの本体には アノテーション違反をエラーにする機能はない。IDEなどが勝手に調べてくれることもあるが、自分でやるなら
- 静的解析:コードを調べて実行前にわかるエラーを検知してもらう
- ランタイム解析:実行時に型があっていなかったらエラーを発生させる
静的解析はmypyなどのツールがある。一般的な開発には静的解析で十分であることが多いが、 ユーザ入力があるシステムなどでは何が飛んでくるかわからないデータに動的に型検証を書ける必要性があるだろう。
ランタイム解析はpydanticやbeartypeなどのライブラリがある。
$ pip install mypy $ cat sample.py n: int = 0.1 # int型にfloatを入れているのでアノテーション違反 $ python -m mypy sample.py sample.py:1: error: Incompatible types in assignment (expression has type "float", variable has type "int") [assignment] Found 1 error in 1 file (checked 1 source file)のように確かにアノテーション違反を検知してくれる。
おすすめ記事
型アノテーション:Python3.8以下でTypeError: 'type' object is not subscriptableがでるFastAPI入門:最小構成でとりあえずサーバを立ててみる
kill -0 [PID] と kill 0: プロセス生存確認とプロセスグループをまとめて終了
同人ゲーム・イラストなど