There are a supertype-like type and subtype-like type:
- I call a type a supertype-like type if the type isn't actually a supertype of other type but the type accepts the other type like the type is a supertype of the other type. For example,
complexisn't actually a supertype offloat,intandboolbutcomplexaccepts them likecomplexis a supertype of them socomplexis a supertype-like type offloat,intandbool. - I call a type a subtype-like type if the type isn't actually a subtype of other type but the type is accepted by the other type like the type is a subtype of the other type. For example,
boolisn't actually a subtype ofcomplexandfloatbutboolis accepted by them likeboolis a subtype of them soboolis a subtype-like type ofcomplexandfloat.
The new syntax of generics is name[parameter, ...] which can be used from Python 3.12:
-
name(Required):- It's an identifier.
-
[parameter, ...](Required):- A
parameteris a (generic) type parameter. -
parametermust be at least one so the empty type parameter[]cannot be used. - For a
parameter, a positional type argument can be set but a keyword type argument cannot be set. -
parameteris*/**name:bound/constraints=default:-
*/**(Optional):- It cannot be both
*and**. - Without
*/**,parameteris TypeVar base. - With
*,parameteris TypeVarTuple base. - With
**,parameteris ParamSpec base. - The old syntax
TypeVar,TypeVarTupleandParamSpeccan still be used. - For
TypeVarbase, variance is automatically inferred. - For
TypeVarTupleandParamSpecbase, variance is always invariant according to the doc but they can be covariant and contravariant, and contravariant according to the issue and the issue respectively.
- It cannot be both
-
name(Required):- It's an identifier.
-
:bound/constraints(Optional):- It cannot be both
boundandconstraints. - It isn't supported for
TypeVarTupleandParamSpecbase by Python interpreter and type checkers. -
bound(Type:Type):- For
TypeVarbase:- It's a type.
- The type, its subtype and subtype-like type can be accepted as type arguments.
- For
-
constraints(Type:tuple(Type)):- For
TypeVar:- It must be at least two types.
- The types can be but their subtypes and subtype-like types cannot be accepted as type arguments.
- For
- It cannot be both
-
=default(Optional):-
default(Type:Type):- For
TypeVar:- It's a default type.
- If
constraintsis set, it must be one type ofconstraintsbut not their subtype or subtype-like type. - If
boundis set, it must be the type, subtype or subtype-like type ofbound.
- For
TypeVarTuplebase:- It's a default unpacked tuple.
- It doesn't work according to the issue.
- For
ParamSpec:- It's a list of default types.
- If
boundis set, it must be the types, subtypes and subtype-like types ofboundin a list.
- For
-
-
- A
The old syntax of generics is TypeVar, TypeVarTuple and ParamSpec:
- For
TypeVar:- The 1st parameter is
name(Required-Type:str):- It's an identifier.
- It must be the same name as its variable.
- In convention,
T.*is used likeT,T1,T2, etc.
- The 2nd parameter is
*constraints(Optional-Type:Type):- It's two or more types.
- The types can be but their subtypes and subtype-like types cannot be accepted as type arguments.
- Don't use any keywords like
*constraints=,constraints=, etc.
- The 3rd parameter is
bound(Optional-Default:None-Type:Type):- It's a type.
- The type, its subtype and subtype-like type can be accepted as type arguments.
- The 4th parameter is
covariant(Optional-Default:False-Type:bool). - The 5th parameter is
contravariant(Optional-Default:False-Type:bool). - The 6th parameter is
infer_variance(Optional-Default:False-Type:bool). - The 7th parameter is
default(Optional-Default:typing.NoDefault-Type:Type):- It's a default type.
- If
*constraintsis set, it must be one type of*constraintsbut not their subtype or subtype-like type. - If
boundis set, it must be the type, subtype or subtype-like type ofbound.
- Only one of
*constraintsorboundcan be set but not two. - By default, variance is invariant.
- Only one of
covariant,contravariantorinfer_variancecan be set but not more than one. - mypy doesn't support
infer_varianceaccording to the issue.
- The 1st parameter is
- For
TypeVarTuple:- The 1st parameter is
name(Required-Type:str):- It's an identifier.
- It must be the same name as its variable.
- In convention,
Ts.*is used likeTs,Ts1,Ts2, etc.
- The 2nd parameter is
default(Optional-Default:typing.NoDefault-Type:Type): - Variance is always invariant according to the doc but it can be covariant and contravariant according to the issue.
- The 1st parameter is
- For
ParamSpec:- The 1st parameter is
name(Required-Type:str):- It's an identifier.
- It must be the same name as its variable.
- In convention,
P.*is used likeP,P1,P2, etc.
- The 2nd parameter is
bound(Optional-Default:None-Type:Type) (Not supported):- It's a list of types.
- The types in a list, their subtypes and subtype-like types can be accepted as type arguments.
- The 3rd parameter is
covariant(Optional-Default:False-Type:bool) (Not supported). - The 4th parameter is
contravariant(Optional-Default:False-Type:bool) (Not supported). - The 5th parameter is
default(Optional-Default:typing.NoDefault-Type:Type):- It's a list of default types.
- If
boundis set, it must be the types, subtypes and subtype-like types ofboundin a list.
- Type checkers don't support
bound,covariantandcontravariant. - Variance is always invariant according to the doc but it can be contravariant according to the issue.
- With mypy, using
namekeyword argument gets error, which is a bug according to the issue.
- The 1st parameter is
The generic type syntax of generics is type[argument, ...] for both the new and old syntax of generics:
-
type(Required-Type:Type):- It's a type.
-
[argument, ...](Optional):- An
argumentis a (generic) type argument. -
argumentmust be at least one so the empty type argument[]cannot be used. - An
argumentmust a positional type argument but not a keyword type argument. - It can be omitted as long as:
- all the type parameters have default types whether mypy is run with or without
--strict. - or mypy is run without
--strictwhether all the type parameters have default types or not:- Any is set to the type parameters without default types.
- all the type parameters have default types whether mypy is run with or without
-
argument(Required-Type:Type):- It's a type.
- An
Variance:
- is the feature of generics to decide whether a type accepts its supertypes, supertype-like types, subtypes and subtype-like types in addition to the type:
- has 3 kinds invariance, covariance and contravariance:
- Invariance only makes a type accept the type.
- Covariance makes a type accept the type, its subtypes and subtype-like types.
- Contravariance make a type accept the type, its supertypes and supertype-like types.
- is inferred for only
TypeVar(base) in the new and old syntax of generics:- In the old syntax, variance inference happens if
infer_variance=Trueis set toTypeVar:- mypy doesn't support
infer_varianceaccording to the issue.
- mypy doesn't support
-
TypeVarTupleandParamSpec(base) are alwaysinvariantaccording to the doc but they can be covariant and contravariant, and contravariant according to the issue and the issue respectively.
- In the old syntax, variance inference happens if
- This is how variance is inferred:
- Covariant if the variable in a class can be read but cannot be written from outside the class.
- Contravariant if the variable in a class can be written but cannot be read from outside the class.
- Invariant if the variable in a class can be read and written from outside the class.
| Inferred Variance | Reason | Example |
|---|---|---|
| Covariant | Read-Only | tuple |
| Contravariant | Write-Only | Callable |
| Invariant | Read-Write | list |
Top comments (0)