Constructors ============ Calls to constructors require special handling within type checkers. Constructor Calls ----------------- At runtime, a call to a class' constructor typically results in the invocation of three methods in the following order: #. The ``__call__`` method of the metaclass (which is typically supplied by the ``type`` class but can be overridden by a custom metaclass and which is responsible for calling the next two methods) #. The ``__new__`` static method of the class #. The ``__init__`` instance method of the class Type checkers should mirror this runtime behavior when analyzing a constructor call. Metaclass ``__call__`` Method ----------------------------- When evaluating a constructor call, a type checker should first check if the class has a custom metaclass (a subclass of ``type``) that defines a ``__call__`` method. If so, it should evaluate the call of this method using the supplied arguments. If the metaclass is ``type``, this step can be skipped. If the evaluated return type of the ``__call__`` method indicates something other than an instance of the class being constructed, a type checker should assume that the metaclass ``__call__`` method is overriding ``type.__call__`` in some special manner, and it should not attempt to evaluate the ``__new__`` or ``__init__`` methods on the class. For example, some metaclass ``__call__`` methods are annotated to return ``NoReturn`` to indicate that constructor calls are not supported for that class. :: 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) If no return type annotation is provided for ``__call__``, a type checker may assume that it does not override ``type.__call__`` in a special manner and proceed as though the return type is an instance of the type specified by the ``cls`` parameter. ``__new__`` Method ------------------ After the metaclass ``__call__`` method has been evaluated, a type checker should evaluate the ``__new__`` method of the class (if applicable) using the supplied arguments. This step should be skipped if the class does not define a ``__new__`` method and does not inherit a ``__new__`` method from a base class other than ``object``. If the class is generic and explicitly specialized, the type checker should partially specialize the ``__new__`` method using the supplied type arguments. If the class is not explicitly specialized, class-scoped type variables should be solved using the supplied arguments passed to the constructor call. :: class MyClass[T]: def __new__(cls, x: T) -> Self: return super().__new__(cls) # Constructor calls for specialized classes assert_type(MyClass[int](1), MyClass[int]) assert_type(MyClass[float](1), MyClass[float]) MyClass[int](1.0) # Type error # Constructor calls for non-specialized classes assert_type(MyClass(1), MyClass[int]) assert_type(MyClass(1.0), MyClass[float]) If any class-scoped type variables are not solved when evaluating the ``__new__`` method call using the supplied arguments, these type variables should be left unsolved, allowing the ``__init__`` method (if applicable) to be used to solve them. :: 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]) For most classes, the return type for the ``__new__`` method is typically ``Self``, but other types are also allowed. For example, the ``__new__`` method may return an instance of a subclass or an instance of some completely unrelated class. If the evaluated return type of ``__new__`` is not the class being constructed (or a subclass thereof), a type checker should assume that the ``__init__`` method will not be called. This is consistent with the runtime behavior of the ``type.__call__`` method. If the ``__new__`` method return type is a union with one or more subtypes that are not instances of the class being constructed (or a subclass thereof), a type checker should likewise assume that the ``__init__`` method will not be called. :: class MyClass: def __new__(cls) -> int: return 0 # In this case, the __init__ method should not be considered # by the type checker when evaluating a constructor call. def __init__(self, x: int): pass assert_type(MyClass(), int) For purposes of this test, an explicit return type of ``Any`` (or a union containing ``Any``) should be treated as a type that is not an instance of the class being constructed. :: class MyClass: def __new__(cls) -> Any: return 0 # The __init__ method will not be called in this case, so # it should not be evaluated. def __init__(self, x: int): pass assert_type(MyClass(), Any) If the return type of ``__new__`` is not annotated, a type checker may assume that the return type is ``Self`` and proceed with the assumption that the ``__init__`` method will be called. If the class is generic, it is possible for a ``__new__`` method to override the specialized class type and return a class instance that is specialized with different type arguments. :: class MyClass[T]: def __new__(cls, *args, **kwargs) -> "MyClass[list[T]]": ... assert_type(MyClass[int](), MyClass[list[int]]) If the ``cls`` parameter within the ``__new__`` method is not annotated, type checkers should infer a type of ``type[Self]``. Regardless of whether the type of the ``cls`` parameter is explicit or inferred, the type checker should bind the class being constructed to the ``cls`` parameter and report any type errors that arise during binding. :: class MyClass[T]: def __new__(cls: "type[MyClass[int]]") -> "MyClass[int]": ... MyClass() # OK MyClass[int]() # OK MyClass[str]() # Type Error ``__init__`` Method ------------------- After evaluating the ``__new__`` method, a type checker should evaluate the ``__init__`` method (if applicable) using the supplied arguments. If the class is generic and explicitly specialized (or specialized via the ``__new__`` method return type), the type checker should partially specialize the ``__init__`` method using the supplied type arguments. If the class is not explicitly specialized, class-scoped type variables should be solved using the supplied arguments passed to the constructor call. This step should be skipped if the class does not define an ``__init__`` method and does not inherit an ``__init__`` method from a base class other than ``object``. :: class MyClass[T]: def __init__(self, x: T) -> None: ... # Constructor calls for specialized classes assert_type(MyClass[int](1), MyClass[int]) assert_type(MyClass[float](1), MyClass[float]) MyClass[int](1.0) # Type error # Constructor calls for non-specialized classes assert_type(MyClass(1), MyClass[int]) assert_type(MyClass(1.0), MyClass[float]) If the ``self`` parameter within the ``__init__`` method is not annotated, type checkers should infer a type of ``Self``. Regardless of whether the ``self`` parameter type is explicit or inferred, a type checker should bind the class being constructed to this parameter and report any type errors that arise during binding. :: class MyClass[T]: def __init__(self: "MyClass[int]") -> None: ... MyClass() # OK MyClass[int]() # OK MyClass[str]() # Type Error The return type for ``__init__`` is always ``None``, which means the method cannot influence the return type of the constructor call by specifying a return type. There are cases where it is desirable for the ``__init__`` method to influence the return type, especially when the ``__init__`` method is overloaded. To enable this, type checkers should allow the ``self`` parameter to be annotated with a type that influences the resulting type of the constructor call. :: 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]) Function-scoped type variables can also be used in the ``self`` annotation of an ``__init__`` method to influence the return type of the constructor call. :: 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]) Class-scoped type variables should not be used in the ``self`` annotation because such use can lead to ambiguous or nonsensical type evaluation results. Type checkers should report an error if a class-scoped type variable is used within a type annotation for the ``self`` parameter in an ``__init__`` method. :: class MyClass4[T1, T2]: # The ``self`` annotation should result in a type error def __init__(self: "MyClass4[T2, T1]") -> None: ... Classes Without ``__new__`` and ``__init__`` Methods ---------------------------------------------------- If a class does not define a ``__new__`` method or ``__init__`` method and does not inherit either of these methods from a base class other than ``object``, a type checker should evaluate the argument list using the ``__new__`` and ``__init__`` methods from the ``object`` class. :: class MyClass5: pass MyClass5() # OK MyClass5(1) # Type error Constructor Calls for type[T] ----------------------------- When a value of type ``type[T]`` (where ``T`` is a concrete class or a type variable) is called, a type checker should evaluate the constructor call as if it is being made on the class ``T`` (or the class that represents the upper bound of type variable ``T``). This means the type checker should use the ``__call__`` method of ``T``'s metaclass and the ``__new__`` and ``__init__`` methods of ``T`` to evaluate the constructor call. It should be noted that such code could be unsafe because the type ``type[T]`` may represent subclasses of ``T``, and those subclasses could redefine the ``__new__`` and ``__init__`` methods in a way that is incompatible with the base class. Likewise, the metaclass of ``T`` could redefine the ``__call__`` method in a way that is incompatible with the base metaclass. Specialization During Construction ---------------------------------- As discussed above, if a class is generic and not explicitly specialized, its type variables should be solved using the arguments passed to the ``__new__`` and ``__init__`` methods. If one or more type variables are not solved during these method evaluations, they should take on their default values. :: 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]) Consistency of ``__new__`` and ``__init__`` ------------------------------------------- Type checkers may optionally validate that the ``__new__`` and ``__init__`` methods for a class have consistent signatures. :: class MyClass: def __new__(cls) -> Self: return super().__new__(cls) # Type error: __new__ and __init__ have inconsistent signatures def __init__(self, x: str) -> None: pass Converting a Constructor to Callable ------------------------------------ Class objects are callable, which means they are compatible with callable types. :: 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`` When converting a class to a callable type, a type checker should use the following rules, which reflect the same rules specified above for evaluating constructor calls: 1. If the class has a custom metaclass that defines a ``__call__`` method that is annotated with a return type other than a subclass of the class being constructed (or a union that contains such a type), a type checker should assume that the metaclass ``__call__`` method is overriding ``type.__call__`` in some special manner. In this case, the callable should be synthesized from the parameters and return type of the metaclass ``__call__`` method after it is bound to the class, and the ``__new__`` or ``__init__`` methods (if present) should be ignored. This is an uncommon case. In the more typical case where there is no custom metaclass that overrides ``type.__call__`` in a special manner, the metaclass ``__call__`` signature should be ignored for purposes of converting to a callable type. If a custom metaclass ``__call__`` method is present but does not have an annotated return type, type checkers may assume that the method acts like ``type.__call__`` and proceed to the next step. 2. If the class defines a ``__new__`` method or inherits a ``__new__`` method from a base class other than ``object``, a type checker should synthesize a callable from the parameters and return type of that method after it is bound to the class. 3. If the return type of the method in step 2 evaluates to a type that is not a subclass of the class being constructed (or a union that includes such a class), the final callable type is based on the result of step 2, and the conversion process is complete. The ``__init__`` method is ignored in this case. This is consistent with the runtime behavior of the ``type.__call__`` method. 4. If the class defines an ``__init__`` method or inherits an ``__init__`` method from a base class other than ``object``, a callable type should be synthesized from the parameters of the ``__init__`` method after it is bound to the class instance resulting from step 2. The return type of this synthesized callable should be the concrete value of ``Self``. 5. If step 2 and 4 both produce no result because the class does not define or inherit a ``__new__`` or ``__init__`` method from a class other than ``object``, the type checker should synthesize callable types from the ``__new__`` and ``__init__`` methods for the ``object`` class. 6. Steps 2, 4 and 5 will produce either one or two callable types. The final result of the conversion process is the union of these types. This will reflect the callable signatures of the applicable ``__new__`` and ``__init__`` methods. :: 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`` If the ``__init__`` or ``__new__`` method is overloaded, the callable type should be synthesized from the overloads. The resulting callable type itself will be overloaded. :: 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`` If the class is generic, the synthesized callable should include any class-scoped type parameters that appear within the signature, but these type parameters should be converted to function-scoped type parameters for the callable. Any function-scoped type parameters in the ``__init__`` or ``__new__`` method should also be included as function-scoped type parameters in the synthesized callable. :: 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]``