PythonのmypyでUnionを使うとき


mypyとは

↑のReadmeの説明を翻訳すると以下の通り。

Mypyは、Python用のオプションの静的型チェッカーです。
Pythonプログラムに型ヒント(PEP 484)を追加し、mypyを使用して静的に型チェックすることができます。
プログラムを実行せずに、プログラムのバグを見つけてください。

プログラムでは、動的型付けと静的型付けを組み合わせることができます。
レガシーコードのように静的型付けが不便な場合は、いつでも動的型付けに切り替えができます。

PEP484は何かというと、Type Hintsのこと。
PythonはJavaと違い変数や引数の型の指定が不要だが、Typt Hintsの登場により型の明記ができるようになった。
これにより、そのメソッドの引数が何の型が想定されているのか分かるようになる。

TypeHints無し
def hoge(a, b):
    return a / b  # 割り算がされるので、str が入るとエラーになる
TypeHints有り
# 引数が int だと分かるので、str を入れてはいけないと分かる
def hoge(a:int, b:int) -> float:
    return a / b

ただ、Type Hintsを書いていても、プログラムの動きは変わらない(と思う)。
「引数の型が違う!」というエラーにはならない。
⇒ TyptHints有りのコードを実行しても、a / bを実行してエラーを出す。

Type Hintsのチェックをする静的テストツールがmypyである。
これによって呼び出し時の引数不一致が分かるので、想定外の型を引数に渡すバグが減る。

mypyとUnion

Unionは、Type Hintsで指定できる型?の一つ。
引数の型が複数あり得る場合に、指定するもの。
例えば、先ほどの例で int が引数だったが、float も使えるようにしたい場合は次のようになる。

TypeHints有り(intもfloatに対応)
# 引数が int と float に対応している
def hoge(a:Union[int, float], b:Union[int, float]) -> float:
    return a / b

ここまでは分かりやすいが、

  • 引数が、int か str
  • int であれば2で割る
  • str であれば fuga を付ける

ではmypyはどう動くのか?

intとstrが引数の関数
# 引数が int と float に対応している
def hoge(a:Union[int, str]) -> Union[float, str]:
    if type(a) == int:
        return a / 2
    else:
        return a + 'fuga'

上記では、次のエラーになる。

> mypy test.py
test.py:8: error: Unsupported operand types for + ("int" and "str")
test.py:8: note: Left operand is of type "Union[int, str]"

エラーはreturn a + 'fuga'で発生している。
⇒ 要するにtype(a) == intで引数が int の場合分けをしているが、mypyはそれに気づけない。

mypy使用下でUnionを使う場合、isinstance()を使うべし

type()をやめて、isinstance()を使うと・・・

intとstrが引数の関数(mypyクリア)
def hoge(a:Union[int, str]) -> Union[float, str]:
    if isinstance(a, int):
        return a / 2
    else:
        return a + 'fuga'
> mypy test.py
Success: no issues found in 1 source file

最後に

mypyを使う場合、型の判別に使えるのはisinstanceとassertらしい。(assertでどう書くのか分からないが、そういう情報だけ見た)

そもそも、Pythonで型を不定にすることが魅力であるという話も聞いたことがあるので、Type Hintsを積極的に使うことが絶対的正解であるかはわからないが、JavaからPythonに入った人はこれがある方が分かりやすいのかもしれない。

私としても、できるだけ静的チェックが使える方が品質が上がると思うので、私がプロジェクト主導することがあればmypyは使いたい派かなぁ。