Python3.11での型ヒントを用いた基本文法
Python3.5で導入された型ヒントとは
Pythonでは、引数や戻り値・リストの要素には型指定を行わず、自由なIN/OUTが基本というのが元々の考え方だった。
しかし、大規模なシステムなどで複数の開発者により分担してプログラムを作成するにあたり、型の指定がないことで開発の遅れや不具合が起こるといった問題が発生しがちである。
そこで、型ヒントを用いて引数や戻り値の型を明確にし、開発における意思疎通を図ろうという仕組みが生まれた。
実際のプログラムをpython
コマンドで実行する際には影響はないが、mypy
コマンドによる型のチェック機能を行うことでそれぞれの関数やオブジェクトの受け渡しを明確にできる。
mypyコマンドを使ったチェック
mypyコマンドのインストール
pip install mypy
にて、自環境のpythonへmypy
コマンドのインストールが完了する。
mypyコマンドによるチェック
mypy チェック対象のpythonモジュール名.py
にて、型ヒントに合っていないコードの箇所を確認可能となる。
また、VSCodeで拡張機能Pythonが追加されている場合は、下部メニューの言語指定横にある{}
マークより、コード記述時に型チェックを行うかどうかを指定することができる。
型チェック:onに指定した場合は、以下のようにソースコード記述時にエラーマークが表示される。
型ヒントの記述方法
変数への型ヒント(1)
# 変数宣言と初期化
# 変数名: 型ヒント = 初期値
num01: int = 100
num02: float = 3.14
str01: str = 'abc'
bool01: bool = True
# 再代入時に指定の型以外の値を代入しようとするとエラー
num01 = 3.14 # mypyによるチェックエラー
str01 = 100 # mypyによるチェックエラー
ただし、float型へint型の値を再代入するなど、自動型変換が可能な型についてはチェック対象外となる。
num2: float = 3.14
num2 = 100 # これはチェックエラーの対象外
また、型ヒントを用いることにより、初期値を代入しなくても変数の宣言を行うことが可能となる。
# 変数宣言
num99 # コンパイルエラー
num99:int #コンパイルエラーは発生しない
変数への型ヒント(2)
リストや辞書型については、以下のように指定する。
# リスト名: list[要素の型ヒント] = 初期値
# 辞書名: dict[キーの型ヒント, 値の型ヒント] = 初期値
# タプル名: tuple[要素の型ヒント] = 初期値
# セット名: set[要素の型ヒント] = 初期値
list01: list[str] = ['abc', 'xyz', 'ABC']
dict01: dict[str, int] = {'key01': 100, 'key02':200}
tuple01: tuple[int, float, str] = (10, 5.2, 'ABC')
set01: set[str] = {'abc', 'xyz', 'ABC'}
関数の型ヒント
関数では、引数への型ヒントと戻り値の型ヒントを指定することが可能である。
# def 関数名(仮引数1: 型ヒント, 仮引数2:型ヒント・・・) -> 戻り値の型ヒント:
# 関数の処理
def greet_message(name: str) -> str:
# return int(name) # mypyによる戻り値のチェックエラー
return f'Hello, {name}.'
def greet_message2(name: str) -> str:
return int(name) # mypyによる戻り値のチェックエラー
def greet_message3(name: str) -> str:
print(f'Hello, {name}.')
# mypyによる戻り値のチェックエラー
print(greet_message('Yamada')) # 「Hello, Yamada.」と表示される
print(greet_message(100)) # mypyによる引数のチェックエラー
複数パターンの型ヒント
型ヒントでは、1つの型だけでなく複数の型を指定することが可能。
#変数名: 型ヒント1 | 型ヒント2 ・・・
sample: str | int
sample = 'abc' # mypyによるチェックはOK
sample = 100 # mypyによるチェックはOK
sample = 3.14 # mypyによるチェックエラー
関数の引数や戻り値を複数の型ヒントを利用することも可能。
def int_or_str(arg: str | int) -> str | int:
if isinstance(arg, int): # 引数がint型なら10倍して返す
return arg * 10
if isinstance(arg, str): # 引数がstr型なら「Hello, 引数の値.」と表示
return f'Hello, {arg}.'
print(int_or_str(5)) # 「50」と表示
print(int_or_str('Yamada')) # 「Hello, Yamada.」と表示
例外発生時の型ヒント
例外発生時、Python3.9までの場合は例外発生時には戻り値が存在しないため、typing.NoReturn
を指定するとなっていたが、Python3.11からは値を持たない型としてtyping.Never
が用意されている。
from typing import Never
def int_or_none(arg: int | None) -> int | Never:
if arg is None:
raise ValueError('引数なし')
else :
return arg * 10
try:
print(int_or_none(5))
print(int_or_none(None))
except:
print('例外発生')
TypeGuardによる型チェック関数作成
is Noneやisinstance()関数のように、型のチェックを行うための関数がPythonでは用意されている。
isinstance()関数などの組み込み関数による型チェックを実行した後の変数は、指定した型であることが明確になっている。そのため、IDEなどでのチェックでも指定した型のメソッドなどを利用することが可能となっている。
しかし、自作のチェック用関数では、チェック後の型が明確であるとは認識されず、IDEでは「Unknown(データ型不明)」の扱いとなる。
そこで、自作のチェック用関数を行った場合でも型を確定させる方法が、typing.TypeGuardである。
まず、自作のチェック用関数の戻り値を、bool
からtyping.TypeGuard[チェック後に確定して欲しいデータ型]
とする。
def is_all_str_list(values: list) -> TypeGuard[list[str]]:
return all([isinstance(e, str) for e in values])
TypeGuard
を利用することにより、関数の実行後の型が確定する。
型が確定するとIDEでの補完などが利用できるため、開発効率が上がる。
型ヒントの設定ファイル
mypyでは、mypy.ini
などのini
ファイルを用いて設定を行うことができる。
設定ファイルは作成するPythonプロジェクトのルートに配置するのが基本となる。
; [mypy]でmypyの設定であることを指定、ファイル先頭に必須
[mypy]
; グローバル設定を以下に記述
; Pythonのバージョン指定
python_version = 3.11
; 戻り値にAny型(あらゆる型もOK)とする場合は警告とする
warn_return_any = True
; mypyで使用されていない設定があった場合、警告とする
warn_unused_configs = True
; [mypy-モジュールパス]でパッケージ毎・モジュール毎の設定を指定可能
[mypy-foo.bar.*]
; foo.barパッケージのモジュール全てに対する設定を以下に指定
; 戻り値にAny型(あらゆる型もOK)とする場合の警告を無効にする
warn_return_any = False
[mypy-numpy]
; numpyライブラリのモジュールに対する設定を以下に指定
; 型情報を持たないライブラリを使用する場合のエラーを無視する
ignore_missing_imports = True
参考サイト
- mypy.iniの細かい設定などは、上記公式ドキュメントが参考となる。
- 型ヒントの最新情報は公式ドキュメントへ。Python3.9から3.10、3.11と頻繁に変更があるため、自分の環境に合わせた内容を確認する必要がある。
Discussion