Type variables with value restriction
By default, a type variable can be replaced with any type. However, sometimesit’s useful to have a type variable that can only have some specific typesas its value. A typical example is a type variable that can only have valuesstr
and bytes
:
- from typing import TypeVar
- AnyStr = TypeVar('AnyStr', str, bytes)
This is actually such a common type variable that AnyStr
isdefined in typing
and we don’t need to define it ourselves.
We can use AnyStr
to define a function that can concatenatetwo strings or bytes objects, but it can’t be called with otherargument types:
- from typing import AnyStr
- def concat(x: AnyStr, y: AnyStr) -> AnyStr:
- return x + y
- concat('a', 'b') # Okay
- concat(b'a', b'b') # Okay
- concat(1, 2) # Error!
Note that this is different from a union type, since combinationsof str
and bytes
are not accepted:
- concat('string', b'bytes') # Error!
In this case, this is exactly what we want, since it’s not possibleto concatenate a string and a bytes object! The type checkerwill reject this function:
- def union_concat(x: Union[str, bytes], y: Union[str, bytes]) -> Union[str, bytes]:
- return x + y # Error: can't concatenate str and bytes
Another interesting special case is calling concat()
with asubtype of str
:
- class S(str): pass
- ss = concat(S('foo'), S('bar'))
You may expect that the type of ss
is S
, but the type isactually str
: a subtype gets promoted to one of the valid valuesfor the type variable, which in this case is str
. This is thussubtly different from bounded quantification in languages such asJava, where the return type would be S
. The way mypy implementsthis is correct for concat
, since concat
actually returns astr
instance in the above example:
- >>> print(type(ss))
- <class 'str'>
You can also use a TypeVar
with a restricted set of possiblevalues when defining a generic class. For example, mypy uses the typePattern[AnyStr]
for the return value of re.compile()
,since regular expressions can be based on a string or a bytes pattern.