Python のジェネリクス
オブジェクトが持っている属性の型についても記述することができる型です。
例 リスト
例えば、リストについて考えてみます。あるリストは要素に int を持っていますし、あるリストは要素に str を持っています。
lst0 = [0, 1, 2] lst1 = ['a', 'b', 'c']
例えば、そのままだと「list 型」としか言えなかったものが...
lst0: list = [0, 1, 2] lst1: list = ['a', 'b', 'c']
ジェネリクスを使うと 「中身は int 型の List 型」 と言えるようになります。
from typing import List lst0: List[int] = [0, 1, 2] lst1: List[str] = ['a', 'b', 'c']
ジェネリクスの意味は、
ジェネリクスの書き方は、
例 ユーザ定義クラス
◯ before
これをジェネリクスで表現します。
class Vector: def __init__(self, x, y): self.x, self.y = x, y vector0 = Vector(0, 1 ) vector1 = Vector(0.0, 0.1)
◯ after
Generic クラスを継承したクラスを作成します。 与えたい引数の個数だけ TypeVar クラスのインスタンス化して、Generic クラスの引数に加えます(添字表記の中に加えます)。
from typing import Generic, TypeVar T = TypeVar('T') class Vector(Generic[T]): def __init__(self, x: T, y: T): self.x, self.y = x, y # 中身は int の Vector vector0: Vector[int] = Vector(0, 1) # 中身は float の Vector vector1: Vector[float] = Vector(0.0, 0.1) # 中身は int の Vector と宣言して # float を使うとエラーになる。 vector2: Vector[int] = Vector(0, 0.1) # 中身は float の Vector と宣言して # int を使ってもエラーに **ならない** 。 vector3: Vector[float] = Vector(0.0, 1)
◯ エラーについて
1つだけエラーで弾かれます。
$ mypy sample.py sample.py:19: error: Argument 2 to "Vector" has incompatible type "float"; expected "int" Found 1 error in 1 file (checked 1 source file) $
# これがエラーにならないのは... vector3: Vector[float] = Vector(0.0, 1) # これがエラーにならないことから # 雰囲気が伝わればと... z: float = 2
In Python, certain types are compatible even though they aren’t subclasses of each other. For example,
int
objects are valid wheneverfloat
objects are expected.
Duck type compatibility - mypy
◯ TypeVar について
このようにして名前を与えるのはデバックの際に表示したいからということなのでしょうか。
T = TypeVar('T') assert T.__name__ == 'T'
クラス定義文や関数定義文では自動的に str 型の名前が付与されます。
class C: pass assert C.__name__ == 'C' def f(): pass assert f.__name__ == 'f'
文字列を与えているのは、名前を与えたいからだと思われます。
T = TypeVar('T') assert T.__name__ == 'T' D = type('D', (), {}) assert D.__name__ == 'D' g = lambda x: x**2 g.__name__ = 'g' assert g.__name__ == 'g'
なぜ名前がわざわざ付与されているのでしょうか? PEP 8 で lambda が使用できない理由を思い出してください。
短くも書ける。
ツールが 、型推論してくれるなら、このように短くも書けます。
vector0 = Vector[int](0, 1) vector1 = Vector[float](0.0, 0.1) vector2 = Vector[int](0, 0.1) # これはエラーになる。 vector3 = Vector[float](0.0, 1)
mypy なら型推論してくれます。
Mypy considers the initial assignment as the definition of a variable. If you do not explicitly specify the type of the variable, mypy infers the type based on the static type of the value expression:
Type inference and type annotations - mypy