概要
型ヒント をちょっと(ちょっとだけ)触ったのでメモ程度にまとめます
ちゃんと理解したい人は以下のドキュメントを読んだ方が早いと思います。
型ヒント
Python は動的型付け言語なので、変数の型を宣言する必要がありません。 実行時にその変数の使われ方からその場その場で型を解釈してくれます。
似た言葉として 型推論 というものもありますが、動的型付けと型推論は別物です。 型推論は静的型付け言語において、つまり初めから型が確定していないといけない言語において、コード内で明示的に型を宣言していなくてもコンパイル時に型を推論してくれる機能です。
型ヒントの書き方ですが、まず関数を定義するときに引数と戻り値の型を書くものがあります。
def fuga(a:int, b: str) -> dict: return {a: b}
こういう感じです。
変数名; 変数の型
と -> 戻り値の型
を書きます。戻り値がない場合は -> None
ですね。
これ自体は何も import しなくても書けます。
ただし、この関数を次のように呼び出しても( Python という言語そのものの仕様のため)特にエラーにはならないので注意が必要です。
fuga('a', 'b')
クラスを定義するときは self
の型って何になるんだろうと思ったらこの場合は書かないみたいです。
まぁ仕組み上間違えることはないですからね。
class Animal: def __init__(self, __name: str) -> None: self.__name = __name def greet(self) -> str: return self.__name
変数の宣言もできます。
def fuga(a:int, b: str) -> dict: c: int c = 2 return {a + c: b}
普通 Python には変数の宣言はなく、いきなり代入から始まります。 上の例でいうと普通はいきなり以下のように書きますよね。
c = 2
また、
c : int = 2
のような書き方もできます。
ただ、型が dict だと宣言していてもキーと値の型が想定したものになっているかはまた別の話として存在します。
標準ライブラリの typing
を import します。
先ほどの例はこのようになります。
from typing import Dict def fuga(a:int, b: str) -> Dict[int, str]: c: int c = 2 return {a + c: b}
Dict[キーの型, 値の型]
となります。
list 型の場合は List[要素の型]
となりますが list 型って複数の型を入れられるのでその場合はどう表現したら良いんですかね?
List[int, srt]
はエラーになる( []
に入れられるのは一つだけ)のでその場合はどうするんですかね?
型エイリアス
それなりに複雑な構造を持った型を変数として何回も使う場合、毎回書いてると見通しがよくありませんし間違いの元にもなります。 そんな場合は型エイリアスを使います。
from typing import Dict Book = Dict[int, str] def hoge(page: int, book: Book) -> str: return book[int]
めちゃくちゃ意味のない関数ですがこのような感じに型にエイリアスを張ることができます。 このエイリアスを使って作った型にまたエイリアスを張って…ということもできるので良い感じにできそうですね。
mypy
Python の型について静的解析するツールが mypy です。
pip install
か pipenv install
なりして使います。後述しますがスクリプト実行時は特に何もしないため、本番環境へのデプロイを前提としたプロジェクトでは(Pipenv のやり方で言えば) pipenv install mypy --dev
のようにして開発用パッケージとして本番環境にはインストールしないようにするのが良いかと思います。 flake8
のようなリントツールや pytest
のようなテスト用ライブラリの時と一緒ですね。
使い方は README を見ていただければわかるように簡単です。 チェックしたい対象に対して
$ mypy hoge.py Success: no issues found in 1 source file
のように実行するだけです。 出力で解析結果が得られます。 関数における型の定義、関数の使い方についてちゃんと宣言した通りの型になっているかをチェックしてくれます。
def func2(a: int, b: str) -> List[int]: return [a, b]
こんなものを用意します。 そして mypy コマンドを実行するとこんな感じで結果が帰ってきます。
$ mypy src/ src/main.py:30: error: List item 1 has incompatible type "str"; expected "int" Found 1 error in 1 file (checked 2 source files)
これは宣言の仕方が間違ってるよ、というものですね
次はこんなのを用意します。 関数 fuga
の第一引数は int だと宣言しているの最後の行で str 型を渡しています。
もちろん dict のキーに str 型は使えますので実行時にエラーは起きませんが、意図した使い方ではないということになります。
def fuga(a:int, b: str) -> Dict[int, str]: c: int c = 2 return {a + c: b} print(fuga('a', 'b'))
mypy コマンドを叩きます。
$ mypy src/ src/main.py:26: error: Argument 1 to "fuga" has incompatible type "str"; expected "int" src/main.py:32: error: Name 'func2' is not defined Found 2 errors in 1 file (checked 2 source files)
Argument 1 to "fuga" has incompatible type "str"; expected "int"
って教えてくれてますね。
あと、先ほどの例で使った関数 func2
の定義をコメントアウトして実行したのに func2
を呼び出している箇所をコメントアウトし忘れていたので、そこのミスも検出してくれてますね。
型のチェックだけかと思っていましたがこの辺もチェックしてくれるみたいです。
そしてコメントアウトしたところはチェック対象外にしてくれるんですね。
ちなみにこの mypy コマンドを実行すると、実行した階層に .mypy_cache
という隠しディレクトリが作成されます。
中身はこんなのです。自動的に .gitignore
が生成されてそこに *
と書かれているのでコミット対象には入らないようにしてくれています。
$ tree -a .mypy_cache .mypy_cache ├── .gitignore └── 3.8 ├── @plugins_snapshot.json ├── _ast.data.json ├── _ast.meta.json ├── _importlib_modulespec.data.json ├── _importlib_modulespec.meta.json ├── abc.data.json ├── abc.meta.json ├── ast.data.json ├── ast.meta.json ├── builtins.data.json ├── builtins.meta.json ├── codecs.data.json ├── codecs.meta.json ├── collections │ ├── __init__.data.json │ ├── __init__.meta.json │ ├── abc.data.json │ └── abc.meta.json ├── genericpath.data.json ├── genericpath.meta.json ├── generics.data.json ├── generics.meta.json ├── importlib │ ├── __init__.data.json │ ├── __init__.meta.json │ ├── abc.data.json │ └── abc.meta.json ├── io.data.json ├── io.meta.json ├── mmap.data.json ├── mmap.meta.json ├── os │ ├── __init__.data.json │ ├── __init__.meta.json │ ├── path.data.json │ └── path.meta.json ├── posix.data.json ├── posix.meta.json ├── sys.data.json ├── sys.meta.json ├── types.data.json ├── types.meta.json ├── typing.data.json └── typing.meta.json 4 directories, 42 files
json の中身が気になる型は一度ご自身で試してみてください。そっ閉じ系のやつです(つまり見てもわからん)。