Movatterモバイル変換


[0]ホーム

URL:


ContentsMenuExpandLight modeDark modeAuto light/dark, in light modeAuto light/dark, in dark modeSkip to content
mypy 1.16.1 documentation
Logo
mypy 1.16.1 documentation

First steps

Type system reference

Configuring and running mypy

Miscellaneous

Project Links

Back to top

Protocols and structural subtyping

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 classDoginherits classAnimal, it’s a subtype ofAnimal. Instances ofDogcan 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.

Predefined protocols

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.

Simple user-defined protocols

You can define your own protocol class by inheriting the specialProtocolclass:

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().

Defining subprotocols and subclassing protocols

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)

Invariance of protocol attributes

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 theBoxprotocol using@property:

classBox(Protocol):@propertydefcontent(self)->object:...classIntBox:content:intdeftakes_box(box:Box)->None:...takes_box(IntBox(42))# OK

Recursive protocols

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

Using isinstance() with protocols

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.

Callback protocols

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

Predefined protocol reference

Iteration protocols

The iteration protocols are useful in many contexts. For example, they allowiteration of objects in for loops.

collections.abc.Iterable[T]

Theexample above has a simple implementation of an__iter__ method.

def__iter__(self)->Iterator[T]

See alsoIterable.

collections.abc.Iterator[T]

def__next__(self)->Tdef__iter__(self)->Iterator[T]

See alsoIterator.

Collection protocols

Many of these are implemented by built-in container types such aslist anddict, and these are also useful for user-definedcollection objects.

collections.abc.Sized

This is a type for objects that supportlen(x).

def__len__(self)->int

See alsoSized.

collections.abc.Container[T]

This is a type for objects that support thein operator.

def__contains__(self,x:object)->bool

See alsoContainer.

collections.abc.Collection[T]

def__len__(self)->intdef__iter__(self)->Iterator[T]def__contains__(self,x:object)->bool

See alsoCollection.

One-off protocols

These protocols are typically only useful with a single standardlibrary function or class.

collections.abc.Reversible[T]

This is a type for objects that supportreversed(x).

def__reversed__(self)->Iterator[T]

See alsoReversible.

typing.SupportsAbs[T]

This is a type for objects that supportabs(x).T is the type ofvalue returned byabs(x).

def__abs__(self)->T

See alsoSupportsAbs.

typing.SupportsBytes

This is a type for objects that supportbytes(x).

def__bytes__(self)->bytes

See alsoSupportsBytes.

typing.SupportsComplex

This is a type for objects that supportcomplex(x). Note that no arithmetic operationsare supported.

def__complex__(self)->complex

See alsoSupportsComplex.

typing.SupportsFloat

This is a type for objects that supportfloat(x). Note that no arithmetic operationsare supported.

def__float__(self)->float

See alsoSupportsFloat.

typing.SupportsInt

This is a type for objects that supportint(x). Note that no arithmetic operationsare supported.

def__int__(self)->int

See alsoSupportsInt.

typing.SupportsRound[T]

This is a type for objects that supportround(x).

def__round__(self)->T

See alsoSupportsRound.

Async protocols

These protocols can be useful in async code. SeeTyping async/awaitfor more information.

collections.abc.Awaitable[T]

def__await__(self)->Generator[Any,None,T]

See alsoAwaitable.

collections.abc.AsyncIterable[T]

def__aiter__(self)->AsyncIterator[T]

See alsoAsyncIterable.

collections.abc.AsyncIterator[T]

def__anext__(self)->Awaitable[T]def__aiter__(self)->AsyncIterator[T]

See alsoAsyncIterator.

Context manager protocols

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.

contextlib.AbstractContextManager[T]

def__enter__(self)->Tdef__exit__(self,exc_type:type[BaseException]|None,exc_value:BaseException|None,traceback:TracebackType|None)->bool|None

See alsoAbstractContextManager.

contextlib.AbstractAsyncContextManager[T]

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.

On this page

[8]ページ先頭

©2009-2025 Movatter.jp