Special types in annotations¶
Any¶
Any represents an unknown static type.
Every type isassignable toAny, andAny is assignable to everytype.
SeeType system concepts for more discussion ofAny.
A function parameter without an annotation is assumed to be annotated withAny. If a generic type is used without specifying type parameters,they are assumed to beAny:
fromcollections.abcimportMappingdefuse_map(m:Mapping)->None:# Same as Mapping[Any, Any]...
This rule also applies totuple, in annotation context it is equivalenttotuple[Any,...]. As well, a bareCallable in an annotation is equivalent toCallable[...,Any]:
fromcollections.abcimportCallabledefcheck_args(args:tuple)->bool:...check_args(())# OKcheck_args((42,'abc'))# Also OKcheck_args(3.14)# Flagged as error by a type checker# A list of arbitrary callables is accepted by this functiondefapply_callbacks(cbs:list[Callable])->None:...
Any can also be used as a base class. This can be useful foravoiding type checker errors with classes that can duck type anywhere orare highly dynamic.
None¶
When used in a type hint, the expressionNone is consideredequivalent totype(None).
NoReturn¶
Thetyping module provides aspecial formNoReturn to annotate functionsthat never return normally. For example, a function that unconditionallyraises an exception:
fromtypingimportNoReturndefstop()->NoReturn:raiseRuntimeError('no way')
TheNoReturn annotation is used for functions such assys.exit.Static type checkers will ensure that functions annotated as returningNoReturn truly never return, either implicitly or explicitly:
importsysfromtypingimportNoReturndeff(x:int)->NoReturn:# Error, f(0) implicitly returns Noneifx!=0:sys.exit(1)
The checkers will also recognize that the code after calls to such functionsis unreachable and will behave accordingly:
# continue from first exampledefg(x:int)->int:ifx>0:returnxstop()return'whatever works'# Error might be not reported by some checkers# that ignore errors in unreachable blocks
Never¶
Since Python 3.11, thetyping module contains aspecial formNever. It represents the bottom type, a type that represents the empty setof Python objects.
TheNever type is equivalent toNoReturn, which is discussed above.TheNoReturn type is conventionally used in return annotations offunctions, andNever is typically used in other locations, but the twotypes are completely interchangeable.
Special cases forfloat andcomplex¶
Python’s numeric typescomplex,float andint are notsubtypes of each other, but to support common use cases, the typesystem contains a straightforward shortcut:when an argument is annotated as havingtypefloat, an argument of typeint is acceptable; similar,for an argument annotated as having typecomplex, arguments oftypefloat orint are acceptable.
type[]¶
Sometimes you want to talk about class objects, in particular classobjects that inherit from a given class. This can be spelled astype[C] whereC is a class. To clarify: whileC (whenused as an annotation) refers to instances of classC,type[C]refers tosubclasses ofC. (This is a similar distinction asbetweenobject andtype.)
For example, suppose we have the following classes:
classUser:...# Abstract base for User classesclassBasicUser(User):...classProUser(User):...classTeamUser(User):...
And suppose we have a function that creates an instance of one ofthese classes if you pass it a class object:
defnew_user(user_class):user=user_class()# (Here we could write the user object to a database)returnuser
Without subscriptingtype[] the best we could do to annotatenew_user()would be:
defnew_user(user_class:type)->User:...
However usingtype[] and a type variable with an upper bound wecan do much better:
defnew_user[U:User](user_class:type[U])->U:...
Now when we callnew_user() with a specific subclass ofUser atype checker will infer the correct type of the result:
joe=new_user(BasicUser)# Inferred type is BasicUser
The value corresponding totype[C] must be an actual class objectthat’s a subtype ofC, not aspecial form or other kind of type.In other words, in theabove example calling e.g.new_user(BasicUser|ProUser) isrejected by the type checker (in addition to failing at runtimebecause you can’t instantiate a union).
Note that it is legal to use a union of classes as the parameter fortype[], as in:
defnew_non_team_user(user_class:type[BasicUser|ProUser]):user=new_user(user_class)...
type[] distributes over unions:type[A|B] isequivalent totype[A]|type[B].
However, the actual argument passed in at runtime must still be aconcrete class object, e.g. in the above example:
new_non_team_user(ProUser)# OKnew_non_team_user(TeamUser)# Disallowed by type checker
type[Any] is also supported (see below for its meaning).
type[T] whereT is a type variable is allowed when annotating thefirst argument of a class method (see the relevant section).
Any other special constructs liketuple orCallable are not allowedas an argument totype.
There are some concerns with this feature: for example whennew_user() callsuser_class() this implies that all subclassesofUser must support this in their constructor signature. Howeverthis is not unique totype[]: class methods have similar concerns.A type checker ought to flag violations of such assumptions, but bydefault constructor calls that match the constructor signature in theindicated base class (User in the example above) should beallowed. A program containing a complex or extensible class hierarchymight also handle this by using a factory class method.
Whentype is parameterized it requires exactly one parameter.Plaintype without brackets, the root of Python’s metaclasshierarchy, is equivalent totype[Any].
Regarding the behavior oftype[Any] (ortype),accessing attributes of a variable with this type only providesattributes and methods defined bytype (for example,__repr__() and__mro__). Such a variable can be called witharbitrary arguments, and the return type isAny.
type is covariant in its parameter, becausetype[Derived] is asubtype oftype[Base]:
defnew_pro_user(pro_user_class:type[ProUser]):user=new_user(pro_user_class)# OK...