inferred base type arguments¶
a generic base written without type arguments takes them from the members the class overrides:
class Box[T]:
item: T
def get(self) -> T:
return self.item
class IntBox(Box):
item = 1
reveal_type(IntBox().get()) # int
transpiles to:
class Box[T]:
item: T
def get(self) -> T:
return self.item
class IntBox(Box[int]):
item: int = 1
reveal_type(IntBox().get())
every member a class writes overrides the one its base declares, so it has to fit the base's declaration. the type arguments the base is inherited at are the ones that make every override fit, solved the way a call solves a generic function's type parameters from its arguments
what counts¶
an attribute counts by its annotation, or by the type its value declares —
item = 1 declares item: int. an attribute that can be assigned through the
base has to match its declaration exactly, since a write through the base could
otherwise store a value the subclass rules out
a method counts by the types it writes itself, its annotated parameters and its annotated return type:
class Store[K, V]:
def get(self, key: K) -> V:
raise NotImplementedError
class Names(Store): # Store[int, str]
override def get(self, key: int) -> str:
return ""
what a method leaves out says nothing about the base: an unannotated parameter
takes its type from the method it overrides, and a return type it does not
write is read off its body, which is typed in terms of the base being solved.
a decorated method, a constructor, a name-mangled __name and a member under a
visibility keyword do not count either
a type parameter with a default takes what the overrides solve it to, and its default when none of them names it
when the arguments are not inferred¶
the base needs its type arguments written, and missing-type-argument says so,
when:
-
a type parameter without a default is named by no override
-
no one specialization fits every override:
-
an override is typed by something the class header cannot name — a method's
Self, a method's own type parameter, an anonymoussomeparameter — or by the class being defined, whose base is what is being solved -
the base cannot be subscripted as the class is made: a stub can call a class generic that is not, like
map
a class's own type parameters can be named, so class H[U](Box): item: U
inherits Box[U]. type arguments written in the header are used as written
editor support¶
the inferred arguments are shown as an inlay hint after the base, under the
inferredBaseTypeArguments setting