コンストラクタ ========================================================================================== コンストラクタの呼び出しは、型チェッカー内で特別な処理が必要です。 コンストラクタの呼び出し ------------------------------------------------------------------------------------------ 実行時にクラスのコンストラクタを呼び出すと、通常、次の順序で 3 つのメソッドが呼び出されます。 #. メタクラスの ``__call__`` メソッド(通常は ``type`` クラスによって提供されますが、カスタムメタクラスによってオーバーライドされることがあり、次の 2 つのメソッドを呼び出す責任があります) #. クラスの ``__new__`` 静的メソッド #. クラスの ``__init__`` インスタンスメソッド 型チェッカーは、コンストラクタ呼び出しを分析する際に、この実行時の動作を反映する必要があります。 メタクラス ``__call__`` メソッド ------------------------------------------------------------------------------------------ コンストラクタ呼び出しを評価する際に、型チェッカーは最初にクラスに ``__call__`` メソッドを定義するカスタムメタクラス(``type`` のサブクラス)があるかどうかを確認する必要があります。 もしそうであれば、提供された引数を使用してこのメソッドの呼び出しを評価する必要があります。 メタクラスが ``type`` である場合、このステップはスキップできます。 ``__call__`` メソッドの評価された戻り値の型が、構築されているクラスのインスタンス以外のものであることを示している場合、型チェッカーはメタクラス ``__call__`` メソッドが特別な方法で ``type.__call__`` をオーバーライドしていると仮定し、クラスの ``__new__`` または ``__init__`` メソッドを評価しないようにする必要があります。 たとえば、いくつかのメタクラス ``__call__`` メソッドは、コンストラクタ呼び出しがそのクラスではサポートされていないことを示すために ``NoReturn`` を返すように注釈されています。 :: class Meta(type): def __call__(cls, *args, **kwargs) -> NoReturn: raise TypeError("Cannot instantiate class") class MyClass(metaclass=Meta): def __new__(cls, *args, **kwargs) -> Self: return super().__new__(cls, *args, **kwargs) assert_type(MyClass(), Never) ``__call__`` に戻り値の型注釈が提供されていない場合、型チェッカーはそれが特別な方法で ``type.__call__`` をオーバーライドしていないと仮定し、戻り値の型が ``cls`` パラメータで指定された型のインスタンスであるかのように進めることができます。 ``__new__`` メソッド ------------------------------------------------------------------------------------------ メタクラス ``__call__`` メソッドが評価された後、型チェッカーはクラスの ``__new__`` メソッド(該当する場合)を提供された引数を使用して評価する必要があります。 クラスが ``__new__`` メソッドを定義しておらず、``object`` 以外の基底クラスから ``__new__`` メソッドを継承していない場合、このステップはスキップする必要があります。 クラスがジェネリックで明示的に特殊化されている場合、型チェッカーは提供された型引数を使用して ``__new__`` メソッドを部分的に特殊化する必要があります。 クラスが明示的に特殊化されていない場合、クラススコープの型変数はコンストラクタ呼び出しに渡された提供された引数を使用して解決する必要があります。 :: class MyClass[T]: def __new__(cls, x: T) -> Self: return super().__new__(cls) # 特殊化されたクラスのコンストラクタ呼び出し assert_type(MyClass[int](1), MyClass[int]) assert_type(MyClass[float](1), MyClass[float]) MyClass[int](1.0) # 型エラー # 非特殊化クラスのコンストラクタ呼び出し assert_type(MyClass(1), MyClass[int]) assert_type(MyClass(1.0), MyClass[float]) 提供された引数を使用して ``__new__`` メソッド呼び出しを評価する際に、クラススコープの型変数が解決されない場合、これらの型変数は解決されないままにしておき、``__init__`` メソッド(該当する場合)を使用してそれらを解決できるようにする必要があります。 :: class MyClass[T]: def __new__(cls, *args, **kwargs) -> Self: return super().__new__(cls) def __init__(self, x: T) -> None: pass assert_type(MyClass(1), MyClass[int]) assert_type(MyClass(""), MyClass[str]) ほとんどのクラスでは、``__new__`` メソッドの戻り値の型は通常 ``Self`` ですが、他の型も許可されます。 たとえば、``__new__`` メソッドはサブクラスのインスタンスや完全に無関係なクラスのインスタンスを返すことができます。 ``__new__`` の評価された戻り値の型が構築されているクラス(またはそのサブクラス)でない場合、型チェッカーは ``__init__`` メソッドが呼び出されないと仮定する必要があります。 これは ``type.__call__`` メソッドの実行時の動作と一致しています。 ``__new__`` メソッドの戻り値の型が構築されているクラス(またはそのサブクラス)でないメンバーを含む共用体である場合、型チェッカーは同様に ``__init__`` メソッドが呼び出されないと仮定する必要があります。 :: class MyClass: def __new__(cls) -> int: return 0 # この場合、型チェッカーはコンストラクタ呼び出しを評価する際に __init__ メソッドを考慮しない必要があります。 def __init__(self, x: int): pass assert_type(MyClass(), int) このテストの目的のために、``Any``(または ``Any`` を含む共用体)の明示的な戻り値の型は、構築されているクラスのインスタンスでない型として扱う必要があります。 :: class MyClass: def __new__(cls) -> Any: return 0 # この場合、__init__ メソッドは呼び出されないため、評価されるべきではありません。 def __init__(self, x: int): pass assert_type(MyClass(), Any) ``__new__`` の戻り値の型が注釈されていない場合、型チェッカーは戻り値の型が ``Self`` であると仮定し、``__init__`` メソッドが呼び出されると仮定して進めることができます。 クラスがジェネリックである場合、``__new__`` メソッドは特殊化されたクラス型をオーバーライドし、異なる型引数で特殊化されたクラスインスタンスを返すことができます。 :: class MyClass[T]: def __new__(cls, *args, **kwargs) -> "MyClass[list[T]]": ... assert_type(MyClass[int](), MyClass[list[int]]) ``__new__`` メソッド内の ``cls`` パラメータが注釈されていない場合、型チェッカーは ``type[Self]`` の型を推論する必要があります。 ``cls`` パラメータの型が明示的であるか推論されるかに関係なく、型チェッカーは構築されているクラスを ``cls`` パラメータにバインドし、バインド中に発生する型エラーを報告する必要があります。 :: class MyClass[T]: def __new__(cls: "type[MyClass[int]]") -> "MyClass[int]": ... MyClass() # OK MyClass[int]() # OK MyClass[str]() # 型エラー ``__init__`` メソッド ------------------------------------------------------------------------------------------ ``__new__`` メソッドを評価した後、型チェッカーは ``__init__`` メソッド(該当する場合)を提供された引数を使用して評価する必要があります。 クラスがジェネリックで明示的に特殊化されている場合(または ``__new__`` メソッドの戻り値の型を介して特殊化されている場合)、型チェッカーは提供された型引数を使用して ``__init__`` メソッドを部分的に特殊化する必要があります。 クラスが明示的に特殊化されていない場合、クラススコープの型変数はコンストラクタ呼び出しに渡された提供された引数を使用して解決する必要があります。 クラスが ``__init__`` メソッドを定義しておらず、``object`` 以外の基底クラスから ``__init__`` メソッドを継承していない場合、このステップはスキップする必要があります。 :: class MyClass[T]: def __init__(self, x: T) -> None: ... # 特殊化されたクラスのコンストラクタ呼び出し assert_type(MyClass[int](1), MyClass[int]) assert_type(MyClass[float](1), MyClass[float]) MyClass[int](1.0) # 型エラー # 非特殊化クラスのコンストラクタ呼び出し assert_type(MyClass(1), MyClass[int]) assert_type(MyClass(1.0), MyClass[float]) ``__init__`` メソッド内の ``self`` パラメータが注釈されていない場合、型チェッカーは ``Self`` の型を推論する必要があります。 ``self`` パラメータの型が明示的であるか推論されるかに関係なく、型チェッカーは構築されているクラスをこのパラメータにバインドし、バインド中に発生する型エラーを報告する必要があります。 :: class MyClass[T]: def __init__(self: "MyClass[int]") -> None: ... MyClass() # OK MyClass[int]() # OK MyClass[str]() # 型エラー ``__init__`` の戻り値の型は常に ``None`` であり、これはメソッドが戻り値の型を指定することによってコンストラクタ呼び出しの戻り値の型に影響を与えることができないことを意味します。 ``__init__`` メソッドが戻り値の型に影響を与えることが望ましい場合があります。特に、``__init__`` メソッドがオーバーロードされている場合です。 これを可能にするために、型チェッカーは ``self`` パラメータを注釈して、コンストラクタ呼び出しの結果の型に影響を与える型を指定できるようにする必要があります。 :: class MyClass1[T]: @overload def __init__(self: "MyClass1[list[int]]", value: int) -> None: ... @overload def __init__(self: "MyClass1[set[str]]", value: str) -> None: ... @overload def __init__(self, value: T) -> None: ... assert_type(MyClass1(0), MyClass1[list[int]]) assert_type(MyClass1[int](3), MyClass1[int]) assert_type(MyClass1(""), MyClass1[set[str]]) assert_type(MyClass1(3.0), MyClass1[float]) 関数スコープの型変数も ``__init__`` メソッドの ``self`` 注釈に使用して、コンストラクタ呼び出しの戻り値の型に影響を与えることができます。 :: class MyClass2[T1, T2]: def __init__[V1, V2](self: "MyClass2[V1, V2]", value1: V1, value2: V2) -> None: ... assert_type(MyClass2(0, ""), MyClass2[int, str]) assert_type(MyClass2[int, str](0, ""), MyClass2[int, str]) class MyClass3[T1, T2]: def __init__[V1, V2](self: "MyClass3[V2, V1]", value1: V1, value2: V2) -> None: ... assert_type(MyClass3(0, ""), MyClass3[str, int]) assert_type(MyClass3[str, int](0, ""), MyClass3[str, int]) クラススコープの型変数は ``self`` 注釈に使用すべきではありません。なぜなら、そのような使用は曖昧または意味のない型評価結果をもたらす可能性があるからです。 型チェッカーは、``__init__`` メソッドの ``self`` パラメータの型注釈内でクラススコープの型変数が使用されている場合、エラーを報告する必要があります。 :: class MyClass4[T1, T2]: # ``self`` 注釈は型エラーを引き起こすべきです def __init__(self: "MyClass4[T2, T1]") -> None: ... ``__new__`` および ``__init__`` メソッドのないクラス ------------------------------------------------------------------------------------------ クラスが ``__new__`` メソッドまたは ``__init__`` メソッドを定義しておらず、``object`` 以外の基底クラスからこれらのメソッドを継承していない場合、型チェッカーは ``object`` クラスの ``__new__`` および ``__init__`` メソッドを使用して引数リストを評価する必要があります。 :: class MyClass5: pass MyClass5() # OK MyClass5(1) # 型エラー type[T] のコンストラクタ呼び出し ------------------------------------------------------------------------------------------ ``type[T]`` 型の値(``T`` が具体的なクラスまたは型変数である場合)を呼び出すと、型チェッカーはクラス ``T``(または型変数 ``T`` の上限を表すクラス)で呼び出しが行われているかのようにコンストラクタ呼び出しを評価する必要があります。 これは、型チェッカーが ``T`` のメタクラスの ``__call__`` メソッドおよび ``T`` の ``__new__`` および ``__init__`` メソッドを使用してコンストラクタ呼び出しを評価する必要があることを意味します。 このようなコードは安全でない可能性があることに注意する必要があります。なぜなら、``type[T]`` の型は ``T`` のサブクラスを表す可能性があり、それらのサブクラスは基底クラスと互換性のない方法で ``__new__`` および ``__init__`` メソッドを再定義する可能性があるからです。 同様に、``T`` のメタクラスは、基底メタクラスと互換性のない方法で ``__call__`` メソッドを再定義する可能性があります。 構築中の特殊化 ------------------------------------------------------------------------------------------ 前述のように、クラスがジェネリックで明示的に特殊化されていない場合、その型変数は ``__new__`` および ``__init__`` メソッドに渡された引数を使用して解決する必要があります。 1 つ以上の型変数がこれらのメソッド評価中に解決されない場合、それらはデフォルト値を取る必要があります。 :: T1 = TypeVar("T1") T2 = TypeVar("T2") T3 = TypeVar("T3", default=str) class MyClass1(Generic[T1, T2]): def __new__(cls, x: T1) -> Self: ... assert_type(MyClass1(1), MyClass1[int, Any]) class MyClass2(Generic[T1, T3]): def __new__(cls, x: T1) -> Self: ... assert_type(MyClass2(1), MyClass2[int, str]) ``__new__`` および ``__init__`` の一貫性 ------------------------------------------------------------------------------------------ 型チェッカーはオプションで、クラスの ``__new__`` および ``__init__`` メソッドが :term:`consistent` なシグネチャを持っていることを検証できます。 :: class MyClass: def __new__(cls) -> Self: return super().__new__(cls) # 型エラー: __new__ と __init__ のシグネチャが一貫していません def __init__(self, x: str) -> None: pass コンストラクタを Callable に変換する ------------------------------------------------------------------------------------------ クラスオブジェクトは呼び出し可能であり、これはクラスオブジェクトの型が呼び出し可能な型に :term:`assignable` であることを意味します。 :: def accepts_callable[**P, R](cb: Callable[P, R]) -> Callable[P, R]: return cb class MyClass: def __init__(self, x: int) -> None: pass reveal_type(accepts_callable(MyClass)) # ``def (x: int) -> MyClass`` クラスを呼び出し可能な型に変換する際に、型チェッカーは次のルールを使用する必要があります。これらのルールは、コンストラクタ呼び出しを評価するために上記で指定されたルールと同じです。 1. クラスがカスタムメタクラスを持ち、そのメタクラスが構築されているクラスのサブクラス以外の型(またはそのような型を含む共用体)を戻り値として注釈された ``__call__`` メソッドを定義している場合、型チェッカーはメタクラス ``__call__`` メソッドが特別な方法で ``type.__call__`` をオーバーライドしていると仮定する必要があります。 この場合、呼び出し可能な型は、クラスにバインドされた後のメタクラス ``__call__`` メソッドのパラメータと戻り値から合成される必要があり、``__new__`` または ``__init__`` メソッド(存在する場合)は無視される必要があります。 これはまれなケースです。 より一般的なケースでは、特別な方法で ``type.__call__`` をオーバーライドするカスタムメタクラスが存在しない場合、呼び出し可能な型に変換する目的でメタクラス ``__call__`` シグネチャは無視されるべきです。 カスタムメタクラス ``__call__`` メソッドが存在するが、注釈された戻り値の型がない場合、型チェッカーはメソッドが ``type.__call__`` のように動作すると仮定し、次のステップに進むことができます。 2. クラスが ``__new__`` メソッドを定義しているか、``object`` 以外の基底クラスから ``__new__`` メソッドを継承している場合、型チェッカーはクラスにバインドされた後のそのメソッドのパラメータと戻り値から呼び出し可能な型を合成する必要があります。 3. ステップ 2 のメソッドの戻り値の型が構築されているクラスのサブクラス(またはそのようなクラスを含む共用体)でない型に評価される場合、最終的な呼び出し可能な型はステップ 2 の結果に基づき、変換プロセスは完了します。 この場合、``__init__`` メソッドは無視されます。 これは ``type.__call__`` メソッドの実行時の動作と一致しています。 4. クラスが ``__init__`` メソッドを定義しているか、``object`` 以外の基底クラスから ``__init__`` メソッドを継承している場合、呼び出し可能な型は、ステップ 2 の結果として得られたクラスインスタンスにバインドされた後の ``__init__`` メソッドのパラメータから合成される必要があります。 この合成された呼び出し可能な型の戻り値は、``Self`` の具体的な値である必要があります。 5. ステップ 2 および 4 の両方が結果を生成しない場合、クラスが ``object`` 以外のクラスから ``__new__`` または ``__init__`` メソッドを定義または継承していないため、型チェッカーは ``object`` クラスの ``__new__`` および ``__init__`` メソッドから呼び出し可能な型を合成する必要があります。 6. ステップ 2、4、および 5 は、1 つまたは 2 つの呼び出し可能な型を生成します。 変換プロセスの最終結果は、これらの型の共用体です。 これは、適用可能な ``__new__`` および ``__init__`` メソッドの呼び出し可能なシグネチャを反映します。 :: class A: """ No __new__ or __init__ """ pass class B: """ __new__ and __init__ """ def __new__(cls, *args, **kwargs) -> Self: ... def __init__(self, x: int) -> None: ... class C: """ __new__ but no __init__ """ def __new__(cls, x: int) -> int: ... class CustomMeta(type): def __call__(cls) -> NoReturn: raise NotImplementedError("Class not constructable") class D(metaclass=CustomMeta): """ Custom metaclass that overrides type.__call__ """ def __new__(cls, *args, **kwargs) -> Self: """ This __new__ is ignored for purposes of conversion """ pass class E: """ __new__ that causes __init__ to be ignored """ def __new__(cls) -> A: return A.__new__(cls) def __init__(self, x: int) -> None: """ This __init__ is ignored for purposes of conversion """ ... reveal_type(accepts_callable(A)) # ``def () -> A`` reveal_type(accepts_callable(B)) # ``def (*args, **kwargs) -> B | def (x: int) -> B`` reveal_type(accepts_callable(C)) # ``def (x: int) -> int`` reveal_type(accepts_callable(D)) # ``def () -> NoReturn`` reveal_type(accepts_callable(E)) # ``def () -> A`` ``__init__`` または ``__new__`` メソッドがオーバーロードされている場合、呼び出し可能な型はオーバーロードから合成される必要があります。 結果の呼び出し可能な型自体もオーバーロードされます。 :: class MyClass: @overload def __init__(self, x: int) -> None: ... @overload def __init__(self, x: str) -> None: ... reveal_type(accepts_callable(MyClass)) # overload of ``def (x: int) -> MyClass`` and ``def (x: str) -> MyClass`` クラスがジェネリックである場合、合成された呼び出し可能な型にはシグネチャ内に現れるクラススコープの型パラメータが含まれる必要がありますが、これらの型パラメータは呼び出し可能な型の関数スコープの型パラメータに変換される必要があります。 ``__init__`` または ``__new__`` メソッド内の関数スコープの型パラメータも、合成された呼び出し可能な型の関数スコープの型パラメータとして含まれる必要があります。 :: class MyClass[T]: def __init__[V](self, x: T, y: list[V], z: V) -> None: ... reveal_type(accepts_callable(MyClass)) # ``def [T, V] (x: T, y: list[V], z: V) -> MyClass[T]``