First steps
Type system reference
Configuring and running mypy
Miscellaneous
Project Links
The Python type system supports two ways of deciding whether two objects arecompatible as types: nominal subtyping and structural subtyping.
Nominal subtyping is strictly based on the class hierarchy. If classDog
inherits classAnimal
, it’s a subtype ofAnimal
. Instances ofDog
can be used whenAnimal
instances are expected. This form of subtypingis what Python’s type system predominantly uses: it’s easy tounderstand and produces clear and concise error messages, and matches how thenativeisinstance
check works – based on classhierarchy.
Structural subtyping is based on the operations that can be performed with anobject. ClassDog
is a structural subtype of classAnimal
if the formerhas all attributes and methods of the latter, and with compatible types.
Structural subtyping can be seen as a static equivalent of duck typing, which iswell known to Python programmers. SeePEP 544 for the detailed specificationof protocols and structural subtyping in Python.
Thecollections.abc
,typing
and other stdlib modules definevarious protocol classes that correspond to common Python protocols, such asIterable[T]
. If a classdefines a suitable__iter__
method, mypy understands that itimplements the iterable protocol and is compatible withIterable[T]
.For example,IntList
below is iterable, overint
values:
from__future__importannotationsfromcollections.abcimportIterator,IterableclassIntList:def__init__(self,value:int,next:IntList|None)->None:self.value=valueself.next=nextdef__iter__(self)->Iterator[int]:current=selfwhilecurrent:yieldcurrent.valuecurrent=current.nextdefprint_numbered(items:Iterable[int])->None:forn,xinenumerate(items):print(n+1,x)x=IntList(3,IntList(5,None))print_numbered(x)# OKprint_numbered([4,5])# Also OK
Predefined protocol reference lists various protocols defined incollections.abc
andtyping
and the signatures of the corresponding methodsyou need to define to implement each protocol.
Note
typing
also contains deprecated aliases to protocols and ABCs defined incollections.abc
, such asIterable[T]
.These are only necessary in Python 3.8 and earlier, since the protocols incollections.abc
didn’t yet support subscripting ([]
) in Python 3.8,but the aliases intyping
have always supportedsubscripting. In Python 3.9 and later, the aliases intyping
don’t provideany extra functionality.
You can define your own protocol class by inheriting the specialProtocol
class:
fromcollections.abcimportIterablefromtypingimportProtocolclassSupportsClose(Protocol):# Empty method body (explicit '...')defclose(self)->None:...classResource:# No SupportsClose base class!defclose(self)->None:self.resource.release()# ... other methods ...defclose_all(items:Iterable[SupportsClose])->None:foriteminitems:item.close()close_all([Resource(),open('some/file')])# OK
Resource
is a subtype of theSupportsClose
protocol since it definesa compatibleclose
method. Regular file objects returned byopen()
aresimilarly compatible with the protocol, as they supportclose()
.
You can also define subprotocols. Existing protocols can be extendedand merged using multiple inheritance. Example:
# ... continuing from the previous exampleclassSupportsRead(Protocol):defread(self,amount:int)->bytes:...classTaggedReadableResource(SupportsClose,SupportsRead,Protocol):label:strclassAdvancedResource(Resource):def__init__(self,label:str)->None:self.label=labeldefread(self,amount:int)->bytes:# some implementation...resource:TaggedReadableResourceresource=AdvancedResource('handle with care')# OK
Note that inheriting from an existing protocol does not automaticallyturn the subclass into a protocol – it just creates a regular(non-protocol) class or ABC that implements the given protocol (orprotocols). TheProtocol
base class must always be explicitlypresent if you are defining a protocol:
classNotAProtocol(SupportsClose):# This is NOT a protocolnew_attr:intclassConcrete:new_attr:int=0defclose(self)->None:...# Error: nominal subtyping used by defaultx:NotAProtocol=Concrete()# Error!
You can also include default implementations of methods inprotocols. If you explicitly subclass these protocols you can inheritthese default implementations.
Explicitly including a protocol as abase class is also a way of documenting that your class implements aparticular protocol, and it forces mypy to verify that your classimplementation is actually compatible with the protocol. In particular,omitting a value for an attribute or a method body will make it implicitlyabstract:
classSomeProto(Protocol):attr:int# Note, no right hand sidedefmethod(self)->str:...# Literally just ... hereclassExplicitSubclass(SomeProto):passExplicitSubclass()# error: Cannot instantiate abstract class 'ExplicitSubclass'# with abstract attributes 'attr' and 'method'
Similarly, explicitly assigning to a protocol instance can be a way to ask thetype checker to verify that your class implements a protocol:
_proto:SomeProto=cast(ExplicitSubclass,None)
A common issue with protocols is that protocol attributes are invariant.For example:
classBox(Protocol):content:objectclassIntBox:content:intdeftakes_box(box:Box)->None:...takes_box(IntBox())# error: Argument 1 to "takes_box" has incompatible type "IntBox"; expected "Box"# note: Following member(s) of "IntBox" have conflicts:# note: content: expected "object", got "int"
This is becauseBox
definescontent
as a mutable attribute.Here’s why this is problematic:
deftakes_box_evil(box:Box)->None:box.content="asdf"# This is bad, since box.content is supposed to be an objectmy_int_box=IntBox()takes_box_evil(my_int_box)my_int_box.content+1# Oops, TypeError!
This can be fixed by declaringcontent
to be read-only in theBox
protocol using@property
:
classBox(Protocol):@propertydefcontent(self)->object:...classIntBox:content:intdeftakes_box(box:Box)->None:...takes_box(IntBox(42))# OK
Protocols can be recursive (self-referential) and mutuallyrecursive. This is useful for declaring abstract recursive collectionssuch as trees and linked lists:
from__future__importannotationsfromtypingimportProtocolclassTreeLike(Protocol):value:int@propertydefleft(self)->TreeLike|None:...@propertydefright(self)->TreeLike|None:...classSimpleTree:def__init__(self,value:int)->None:self.value=valueself.left:SimpleTree|None=Noneself.right:SimpleTree|None=Noneroot:TreeLike=SimpleTree(0)# OK
You can use a protocol class withisinstance()
if you decorate itwith the@runtime_checkable
class decorator. The decorator addsrudimentary support for runtime structural checks:
fromtypingimportProtocol,runtime_checkable@runtime_checkableclassPortable(Protocol):handles:intclassMug:def__init__(self)->None:self.handles=1defuse(handles:int)->None:...mug=Mug()ifisinstance(mug,Portable):# Works at runtime!use(mug.handles)
isinstance()
also works with thepredefined protocolsintyping
such asIterable
.
Warning
isinstance()
with protocols is not completely safe at runtime.For example, signatures of methods are not checked. The runtimeimplementation only checks that all protocol members exist,not that they have the correct type.issubclass()
with protocolswill only check for the existence of methods.
Note
isinstance()
with protocols can also be surprisingly slow.In many cases, you’re better served by usinghasattr()
tocheck for the presence of attributes.
Protocols can be used to define flexible callback types that are hard(or even impossible) to express using theCallable[...]
syntax,such as variadic, overloaded, and complex generic callbacks. They are defined with aspecial__call__
member:
fromcollections.abcimportIterablefromtypingimportOptional,ProtocolclassCombiner(Protocol):def__call__(self,*vals:bytes,maxlen:int|None=None)->list[bytes]:...defbatch_proc(data:Iterable[bytes],cb_results:Combiner)->bytes:foritemindata:...defgood_cb(*vals:bytes,maxlen:int|None=None)->list[bytes]:...defbad_cb(*vals:bytes,maxitems:int|None)->list[bytes]:...batch_proc([],good_cb)# OKbatch_proc([],bad_cb)# Error! Argument 2 has incompatible type because of# different name and kind in the callback
Callback protocols andCallable
types can be used mostly interchangeably.Parameter names in__call__
methods must be identical, unlessthe parameters are positional-only. Example (using the legacy syntax for generic functions):
fromcollections.abcimportCallablefromtypingimportProtocol,TypeVarT=TypeVar('T')classCopy(Protocol):# '/' marks the end of positional-only parametersdef__call__(self,origin:T,/)->T:...copy_a:Callable[[T],T]copy_b:Copycopy_a=copy_b# OKcopy_b=copy_a# Also OK
The iteration protocols are useful in many contexts. For example, they allowiteration of objects in for loops.
Theexample above has a simple implementation of an__iter__
method.
def__iter__(self)->Iterator[T]
See alsoIterable
.
def__next__(self)->Tdef__iter__(self)->Iterator[T]
See alsoIterator
.
Many of these are implemented by built-in container types such aslist
anddict
, and these are also useful for user-definedcollection objects.
This is a type for objects that supportlen(x)
.
def__len__(self)->int
See alsoSized
.
This is a type for objects that support thein
operator.
def__contains__(self,x:object)->bool
See alsoContainer
.
def__len__(self)->intdef__iter__(self)->Iterator[T]def__contains__(self,x:object)->bool
See alsoCollection
.
These protocols are typically only useful with a single standardlibrary function or class.
This is a type for objects that supportreversed(x)
.
def__reversed__(self)->Iterator[T]
See alsoReversible
.
This is a type for objects that supportabs(x)
.T
is the type ofvalue returned byabs(x)
.
def__abs__(self)->T
See alsoSupportsAbs
.
This is a type for objects that supportbytes(x)
.
def__bytes__(self)->bytes
See alsoSupportsBytes
.
This is a type for objects that supportcomplex(x)
. Note that no arithmetic operationsare supported.
def__complex__(self)->complex
See alsoSupportsComplex
.
This is a type for objects that supportfloat(x)
. Note that no arithmetic operationsare supported.
def__float__(self)->float
See alsoSupportsFloat
.
This is a type for objects that supportint(x)
. Note that no arithmetic operationsare supported.
def__int__(self)->int
See alsoSupportsInt
.
This is a type for objects that supportround(x)
.
def__round__(self)->T
See alsoSupportsRound
.
These protocols can be useful in async code. SeeTyping async/awaitfor more information.
def__await__(self)->Generator[Any,None,T]
See alsoAwaitable
.
def__aiter__(self)->AsyncIterator[T]
See alsoAsyncIterable
.
def__anext__(self)->Awaitable[T]def__aiter__(self)->AsyncIterator[T]
See alsoAsyncIterator
.
There are two protocols for context managers – one for regular contextmanagers and one for async ones. These allow defining objects that canbe used inwith
andasyncwith
statements.
def__enter__(self)->Tdef__exit__(self,exc_type:type[BaseException]|None,exc_value:BaseException|None,traceback:TracebackType|None)->bool|None
See alsoAbstractContextManager
.
def__aenter__(self)->Awaitable[T]def__aexit__(self,exc_type:type[BaseException]|None,exc_value:BaseException|None,traceback:TracebackType|None)->Awaitable[bool|None]
See alsoAbstractAsyncContextManager
.