PythonのType Hintsとmypy
python動的型付け言語であるPythonで型アノテーションを書けるようにするための構文。 PEP 484で提案され、Python 3.5で実装された。 実行には影響せず、mypyのようなツールで静的解析したりするために使われる。
mypyをインストールする。
$ python -m pip install -U mypy
以下のように引数と返り値に型を書くと、型が誤っている場合にmypyで検知できるようになる。
$ cat main.py
def foo(n: int) -> str:
return str(n)
print(foo(3))
print(foo('3'))
$ python main.py
3
3
$ mypy main.py
main.py:5: error: Argument 1 to "foo" has incompatible type "str"; expected "int"
また、Type HintsがないライブラリなどのためにStubファイルを別に作って型を書くこともできるようにもなっている。デフォルトではStubがないモジュールはエラーになってしまうので必要に応じてignore_missing_importする。 mypy.iniやsetup.cfgに設定を書くと自動で使われる。
$ cat main.py
import numpy as np
$ mypy main.py
main.py:1: error: No library stub file for module 'numpy'
main.py:1: note: (Stub files are from https://github.com/python/typeshed)
$ vi mypy.ini
[mypy]
[mypy-numpy]
ignore_missing_imports = True
VSCode上でもmypyを有効にすると表示されるようになる。FormatterやLintの設定と併せて有効にしておくとよい。
PythonのLintとFormatter - sambagiz-net
"python.linting.mypyEnabled": true
Callable
Callable[[引数], 返り値]のように書く。引数を…にすると返り値だけをチェックさせることができる。
from typing import Callable
def foo(f: Callable[[int], None]) -> None:
f(1)
def bar(f: Callable[..., None]) -> None:
f(1)
def f1(x: int):
print(x)
def f2(x: str):
print(x)
foo(f1) # ok
foo(f2) # error: Argument 1 to "foo" has incompatible type "Callable[[str], Any]"; expected "Callable[[int], None]"
bar(f1) # ok
bar(f2) # ok
Generics
from typing import Dict
from typing import TypeVar, Generic
T = TypeVar('T')
class Foo(Generic[T]):
def __init__(self, v: T) -> None:
self.v = v
def getV(self) -> T:
return self.v
m: Dict[str, Foo[int]] = {'a': Foo(1)}
m2: Dict[str, Foo[str]] = {'a': Foo('1')}
def bar(m: Dict[str, Foo[int]], key: str) -> Foo[int]:
return Foo(m[key]).getV()
def bar2(m: Dict[str, Foo[int]], key: str) -> Foo[str]:
return Foo(m[key]).getV() # error: Incompatible return value type (got "Foo[int]", expected "Foo[str]")
bar(m, 'a')
bar(m2, 'a') # error: Argument 1 to "bar" has incompatible type "Dict[str, Foo[str]]"; expected "Dict[str, Foo[int]]"
Union types
いずれかの型の値を取れるUnion typeも書ける。
from typing import Union, Sequence
def foo(e: Union[int, Sequence[int]]) -> Sequence[int]:
if isinstance(e, int):
return [e]
return e
print(foo(1))
print(foo([1]))
Union[T, None]はOptional[T]と同じ。Noneをデフォルト引数にすると自動でOptional[T]として扱われる。
def foo(e: int = None):
if e:
print(e)
foo(1)
foo()
PythonのProtocolによるstructural subtypingでインタフェースを記述する - sambaiz-net