静的型付けの言語の経験がそれほどないというのと、動的型付けである python においてどれほど型に厳格であるべきかという悩みが個人的にはそれなりにある。特に type hints においては単なる静的チェックでしかなく実行時の強制力はない。そのため型のゆるさに目を向ければそれはもう青天井であるとも捉えられる。
python にはダブルアンスコで挟む特殊メソッドを使うことでプロトコルを定義して振る舞いを決めるという慣例があるため厳密に型を付けたいなら適切なメソッドを決める必要があるが、今回は type hints だけに焦点を当てて思いつくものを書きなぐる。
type alias か NewType
か
ある型を type alias とするか NewType
とするかは悩ましいところではあるが、場合によっては NewType
を用いることで文脈を明示することができる。
例えば open
と close
という状態をもつクラスを考えた時、 Open
と Close
という NewType
を定義することで関数のインターフェースとして表現することができる。
import typing class C: def __init__(self): self.state = 'open' def close(self): self.state = 'close' StatefulC = C # 無理やり気味 def typealias(c: StatefulC) -> StatefulC: c.close() return c Open = typing.NewType('Open', C) Close = typing.NewType('Close', C) def newtype(c: Open) -> Close: c.close() return Close(c)
NewType
の定義が煩雑ではあるがそういうもんだろう。
Dict
の使い所
キー名が単なる str
なのであれば正直 Dict
は使いづらい。typescript であれば interface
によってキー名を指定することが可能であるが、Dict
ではそうはいかない。それをやるには python3.6 までは namedtuple
、python3.7 以降であれば dataclass
を使うが吉だろう。
dataclass
が使えるならそれに越したこたないという気持ちではある。
import typing from dataclasses import dataclass, asdict @dataclass class DC: x: int y: str def f(x: DC) -> typing.Dict[str, typing.Union[str, int]]: return asdict(DC(x=1, y='hoge'))
Dict
の使いどころとしては dataclass
の属性にできないデータ型のキーを指定したい場合という事になるだろう。
例えばキーをタプルとした場合を考えてみる。
import typing D = typing.Dict[typing.Tuple[str, str], str] E = typing.Dict[typing.Tuple[str, str, str], int] def f(x: D) -> E: return {(*k, v): 0 for k, v in x.items()}
全く実践的な例でないのでよくわからないが、こういう事だろう。
Optional
をどこでどう使うべきなんだ
現時点の結論としてはプリミティブな値になりうる場合にのみ使うのがいいだろうと思う。その理由を以下につらつらと書く。
rust や scala などには標準の型として Result
や Option
がある。これと似たようなことを python の type hints で表現しようとすると Optional
を使う事になるだろう。こんな感じ。
import typing import enum class E1(Exception): ... class E2(Exception): ... class Reason(enum.Enum): laala_missing_mirei = 'laala missing mirei error' mirei_quit_puri = 'mirei quit puri error' Result = typing.Optional[Reason] def f(func) -> Result: try: func() except E1: return Reason.laala_missing_mirei except E2: return Reason.mirei_quit_puri else: return None
ただこのような文脈であれば result is None
という判定はなぜ None
が正常系の分岐なのかというのが書いた本人が書いた当時にしかわからず非常に微妙。
このような凝った処理を行う際は素直に Result
型を定義するべきだろう。
そして Optional
の使いどころとしては素直に先述した通りの使い方に留めるのがよろしかろう。