Python

typehintとmypyを使ったpythonの型チェック

typehintとmypyを使ったpythonの型チェック

typehintとは?

def add_nums(num1: int, num2: int) -> int:
    return num1 + num2

add_nums(5,3)
>> 8
  • python 3.5以降のみ対応している機能
  • typehintとは関数作成時に、引数・返り値の型を設定しておくことで、関数使用時・あるいはデバッグ時にわかりやすくするための機能
  • 実行時に型チェックをするわけではないので、設定された型に反しいてもエラーも警告も出ず、そのまま実行できる
  • mypyを使った型チェックがデバッグに便利

使い方

  • 冒頭のスクリプトにあるように、関数の引数・返り値の型を設定するのが基本的な使い方。
  • intstrといった通常のtypeだけでなく、以下で紹介するカスタムな型を用いることで、もう少し柔軟な設定も可能となる。

エディタ使用時の注意点

  • SublimeのSublimeLinter-flake8など、構文エラーチェックツールを入れている場合、構文バージョンをpython3.5以上にしないと、typehintの部分がsyntax errorとなってしまう
  • SublimeでSublimeLinter-flake8を使っている場合、以下のように設定を更新する。
    • [基本設定]->[Package Settings]->[Python Flake8 Lint]->[Settings-User]で設定ファイルを開く。
    • "python_interpreter": "auto"となっているautoの部分を自身の使っているpythonの絶対パス(例:"/User/username/.../python")に置き換える。(pythonの絶対パスはターミナルでwhich pythonと入力すれば得られる)

カスタムな型

カスタムな型1: 型エイリアス

  • 型のショートカット(エイリアス)である型。元の型と同値になる。使うと複雑になるだけのでここでは省略。

カスタムな型2: NewType(継承型)

  • ある型を継承したサブクラス型を定義する方法。型エイリアスのように別名の型変数を生成するところは似ているが、作った型は元と同値ではなく、サブクラスになる。
from typing import NewType
Newnum = NewType('Newnum', int)

def return_int(num: int) -> int:
    return num

def return_num(num: Newnum) -> Newnum:
    return num

return_num(Newnum(5)) # 入力型Newnumがtypehintの指定と一致しているのでOK
return_int(Newnum(5)) # 入力型Newnumはtypehintの指定にあるintのサブクラスなのでOK
return_num(5) # 入力型intはtypehintの指定にあるNewnumのスーパークラスなのでNG -> mypy Error
return_num(Newnum("5")) # Newnumはintのサブクラスなので関係のないstrはNG -> mypy Error
  • 最後の例のようにintstrも入れたいNewTypeを作るときは、objectのサブクラスとしてNewTypeを作成する
Newobj = NewType('Newobj', object)
Newobj(5)   # intはobjectのサブクラスなのでOK
Newobj("5") # strはobjectのサブクラスなのでOK

カスタムな型3: TypeVar(型変数)

  • 型の種類を変数として持つことができる
  • 第2引数以降にtypeを与えることで、持つことができる型の種類を制限することができる
  • ただし、1種類に制限することはできない(それならtypeエイリアスを使うべき)
from typing import TypeVar
T = TypeVar('T') # 型変数Tを定義
O2 = TypeVar('O2',int, float) # intかfloatに型の種類を制限
O1 = TypeVar('O1',object) # 1種類の型に制限はダメ
O11 = TypeVar('O11',int,int) # ただし、これはできてしまう
  • TypeVarをジェネリクスに使うこともできる
    • ジェネリクス(総称型): コンテナの中に入れるオブジェクトの型を指定する方法。
from typing import TypeVar, Sequence

T = TypeVar('T', int, float)      # Declare type variable


def first(l: Sequence[T]) -> T:   # Generic function
    return l[0]

print(first([1, 2, 3, 4, 5]))  # mypy OK
print(first(["a", "b"]))  # mypy error: Value of type variable "T" of "first" cannot be "str"

mypyを使った型チェック

  • まず、mypyをpipでインストールする
    pip install mypy

  • 次のようなpythonスクリプトを作成する

def add_nums(num1: int, num2: int) -> int:
    return num1 + num2

add_nums("5", "3")
  • 上記pythonスクリプトをmypyで実行する(mypy [スクリプト名])と次のようなエラーが出て、typeが間違っていることがわかる。なお、typeが間違っていない場合、何も出力されない。
$ mypy check_type.py 
>> check_type.py:4: error: Argument 1 to "add_nums" has incompatible type "str"; expected "int"
>> check_type.py:4: error: Argument 2 to "add_nums" has incompatible type "str"; expected "int"