Generic protocols
Mypy supports generic protocols (see also Protocols and structural subtyping). Severalpredefined protocols are generic, such asIterable[T]
, and you can define additional generic protocols. Genericprotocols mostly follow the normal rules for generic classes. Example:
- from typing import TypeVar
- from typing_extensions import Protocol
- T = TypeVar('T')
- class Box(Protocol[T]):
- content: T
- def do_stuff(one: Box[str], other: Box[bytes]) -> None:
- ...
- class StringWrapper:
- def __init__(self, content: str) -> None:
- self.content = content
- class BytesWrapper:
- def __init__(self, content: bytes) -> None:
- self.content = content
- do_stuff(StringWrapper('one'), BytesWrapper(b'other')) # OK
- x: Box[float] = ...
- y: Box[int] = ...
- x = y # Error -- Box is invariant
The main difference between generic protocols and ordinary genericclasses is that mypy checks that the declared variances of generictype variables in a protocol match how they are used in the protocoldefinition. The protocol in this example is rejected, since the typevariable T
is used covariantly as a return type, but the typevariable is invariant:
- from typing import TypeVar
- from typing_extensions import Protocol
- T = TypeVar('T')
- class ReadOnlyBox(Protocol[T]): # Error: covariant type variable expected
- def content(self) -> T: ...
This example correctly uses a covariant type variable:
- from typing import TypeVar
- from typing_extensions import Protocol
- T_co = TypeVar('T_co', covariant=True)
- class ReadOnlyBox(Protocol[T_co]): # OK
- def content(self) -> T_co: ...
- ax: ReadOnlyBox[float] = ...
- ay: ReadOnlyBox[int] = ...
- ax = ay # OK -- ReadOnlyBox is covariant
See Variance of generic types for more about variance.
Generic protocols can also be recursive. Example:
- T = TypeVar('T')
- class Linked(Protocol[T]):
- val: T
- def next(self) -> 'Linked[T]': ...
- class L:
- val: int
- ... # details omitted
- def next(self) -> 'L':
- ... # details omitted
- def last(seq: Linked[T]) -> T:
- ... # implementation omitted
- result = last(L()) # Inferred type of 'result' is 'int'