PEP 483を読む(続き5)
共変性(Covariance)と反変性(Contravariance)
t2がt1の部分型であるとき、総称型コンストラクタGenTypeはこう呼ばれます。
- 共変 ― すべてのt1、t2においてGenType[t2]がGenType[t1]の部分型であるとき
- 反変 ― すべてのt1、t2においてGenType[t1]がGenType[t2]の部分型であるとき
- 不変(Invariant) ― 上記のどちらでもないとき
反変ってどんなときだ
- Unionは共変的に振舞う。
わかる
- List[T]は不変。List[int]の値の集合はList[float]の値の集合の部分集合だが、List[int]にはintしかappendできない。
List[T]は不変、って知らない人が見たら完全に誤解するな
Invariantは不変としか訳し方思いつかないけど
共変でも反変でもないことはわかる
(どこか直感に反する)反変的なふるまいの
うん
反する反する
最もよい例はcallable型です。
ふむ?
Callable[[float], None]はCallable[[int], None]の部分型です。
んー?
intはfloatの部分型で、
floatを引数に取る関数はintを引数に取る関数の部分型・・・
ほんとだ!反変だ!
もうひとつ例
Callable[[Employee], None]はCallable[[Manager], None]の部分型です。
もうそんなに不思議に見えない
def calculate_all(lst: List[Manager], salary: Callable[[Manager], Decimal])
はManagerの給与を計算する関数を期待しているので、すべての従業員の給与を計算するCallable[[Employee], Decimal]も受け付けます。
それでも「ん?」と思ったのは、Employeeの給与計算の式と
Managerの給与計算の式は違う、っていうイメージからかな
Callable[[Employee], Decimal]はManagerの給与計算もできるってことだよね
それならわかる
なお返り値の方については共変性
関数についてより正確な型アノテーションをするためには、引数についてはもっとも一般的な型を、返り値についてはもっとも特殊な方を選ぶのがよいことがわかります。
いつか使うときにはもう一度あたまひねり直さないとだろうな