Movatterモバイル変換


[0]ホーム

URL:


Following system colour schemeSelected dark colour schemeSelected light colour scheme

Python Enhancement Proposals

PEP 484 – Type Hints

Author:
Guido van Rossum <guido at python.org>, Jukka Lehtosalo <jukka.lehtosalo at iki.fi>, Łukasz Langa <lukasz at python.org>
BDFL-Delegate:
Mark Shannon
Discussions-To:
Python-Dev list
Status:
Final
Type:
Standards Track
Topic:
Typing
Created:
29-Sep-2014
Python-Version:
3.5
Post-History:
16-Jan-2015, 20-Mar-2015, 17-Apr-2015, 20-May-2015, 22-May-2015
Resolution:
Python-Dev message

Table of Contents

Abstract

PEP 3107 introduced syntax for function annotations, but the semanticswere deliberately left undefined. There has now been enough 3rd partyusage for static type analysis that the community would benefit froma standard vocabulary and baseline tools within the standard library.

This PEP introduces a provisional module to provide these standarddefinitions and tools, along with some conventions for situationswhere annotations are not available.

Note that this PEP still explicitly does NOT prevent other uses ofannotations, nor does it require (or forbid) any particular processingof annotations, even when they conform to this specification. Itsimply enables better coordination, asPEP 333 did for web frameworks.

For example, here is a simple function whose argument and return typeare declared in the annotations:

defgreeting(name:str)->str:return'Hello '+name

While these annotations are available at runtime through the usual__annotations__ attribute,no type checking happens at runtime.Instead, the proposal assumes the existence of a separate off-linetype checker which users can run over their source code voluntarily.Essentially, such a type checker acts as a very powerful linter.(While it would of course be possible for individual users to employa similar checker at run time for Design By Contract enforcement orJIT optimization, those tools are not yet as mature.)

The proposal is strongly inspired bymypy. For example, thetype “sequence of integers” can be written asSequence[int]. Thesquare brackets mean that no new syntax needs to be added to thelanguage. The example here uses a custom typeSequence, importedfrom a pure-Python moduletyping. TheSequence[int] notationworks at runtime by implementing__getitem__() in the metaclass(but its significance is primarily to an offline type checker).

The type system supports unions, generic types, and a special typenamedAny which is consistent with (i.e. assignable to and from) alltypes. This latter feature is taken from the idea of gradual typing.Gradual typing and the full type system are explained inPEP 483.

Other approaches from which we have borrowed or to which ours can becompared and contrasted are described inPEP 482.

Rationale and Goals

PEP 3107 added support for arbitrary annotations on parts of afunction definition. Although no meaning was assigned to annotationsthen, there has always been animplicit goal to use them for typehinting, which is listed as the first possible use casein said PEP.

This PEP aims to provide a standard syntax for type annotations,opening up Python code to easier static analysis and refactoring,potential runtime type checking, and (perhaps, in some contexts)code generation utilizing type information.

Of these goals, static analysis is the most important. This includessupport for off-line type checkers such as mypy, as well as providinga standard notation that can be used by IDEs for code completion andrefactoring.

Non-goals

While the proposed typing module will contain some building blocks forruntime type checking – in particular theget_type_hints()function – third party packages would have to be developed toimplement specific runtime type checking functionality, for exampleusing decorators or metaclasses. Using type hints for performanceoptimizations is left as an exercise for the reader.

It should also be emphasized thatPython will remain a dynamicallytyped language, and the authors have no desire to ever make type hintsmandatory, even by convention.

The meaning of annotations

Any function without annotations should be treated as having the mostgeneral type possible, or ignored, by any type checker. Functionswith the@no_type_check decorator should be treated as havingno annotations.

It is recommended but not required that checked functions haveannotations for all arguments and the return type. For a checkedfunction, the default annotation for arguments and for the return typeisAny. An exception is the first argument of instance andclass methods. If it is not annotated, then it is assumed to have thetype of the containing class for instance methods, and a type objecttype corresponding to the containing class object for class methods.For example, in classA the first argument of an instance methodhas the implicit typeA. In a class method, the precise type ofthe first argument cannot be represented using the available typenotation.

(Note that the return type of__init__ ought to be annotated with->None. The reason for this is subtle. If__init__ assumeda return annotation of->None, would that mean that anargument-less, un-annotated__init__ method should still betype-checked? Rather than leaving this ambiguous or introducing anexception to the exception, we simply say that__init__ ought tohave a return annotation; the default behavior is thus the same as forother methods.)

A type checker is expected to check the body of a checked function forconsistency with the given annotations. The annotations may also beused to check correctness of calls appearing in other checked functions.

Type checkers are expected to attempt to infer as much information asnecessary. The minimum requirement is to handle the builtindecorators@property,@staticmethod and@classmethod.

Type Definition Syntax

The syntax leveragesPEP 3107-style annotations with a number ofextensions described in sections below. In its basic form, typehinting is used by filling function annotation slots with classes:

defgreeting(name:str)->str:return'Hello '+name

This states that the expected type of thename argument isstr. Analogically, the expected return type isstr.

Expressions whose type is a subtype of a specific argument type arealso accepted for that argument.

Acceptable type hints

Type hints may be built-in classes (including those defined instandard library or third-party extension modules), abstract baseclasses, types available in thetypes module, and user-definedclasses (including those defined in the standard library orthird-party modules).

While annotations are normally the best format for type hints,there are times when it is more appropriate to represent themby a special comment, or in a separately distributed stubfile. (See below for examples.)

Annotations must be valid expressions that evaluate without raisingexceptions at the time the function is defined (but see below forforward references).

Annotations should be kept simple or static analysis tools may not beable to interpret the values. For example, dynamically computed typesare unlikely to be understood. (This is anintentionally somewhat vague requirement, specific inclusions andexclusions may be added to future versions of this PEP as warranted bythe discussion.)

In addition to the above, the following special constructs definedbelow may be used:None,Any,Union,Tuple,Callable, all ABCs and stand-ins for concrete classes exportedfromtyping (e.g.Sequence andDict), type variables, andtype aliases.

All newly introduced names used to support features described infollowing sections (such asAny andUnion) are available inthetyping module.

Using None

When used in a type hint, the expressionNone is consideredequivalent totype(None).

Type aliases

Type aliases are defined by simple variable assignments:

Url=strdefretry(url:Url,retry_count:int)->None:...

Note that we recommend capitalizing alias names, since they representuser-defined types, which (like user-defined classes) are typicallyspelled that way.

Type aliases may be as complex as type hints in annotations –anything that is acceptable as a type hint is acceptable in a typealias:

fromtypingimportTypeVar,Iterable,TupleT=TypeVar('T',int,float,complex)Vector=Iterable[Tuple[T,T]]definproduct(v:Vector[T])->T:returnsum(x*yforx,yinv)defdilate(v:Vector[T],scale:T)->Vector[T]:return((x*scale,y*scale)forx,yinv)vec=[]# type: Vector[float]

This is equivalent to:

fromtypingimportTypeVar,Iterable,TupleT=TypeVar('T',int,float,complex)definproduct(v:Iterable[Tuple[T,T]])->T:returnsum(x*yforx,yinv)defdilate(v:Iterable[Tuple[T,T]],scale:T)->Iterable[Tuple[T,T]]:return((x*scale,y*scale)forx,yinv)vec=[]# type: Iterable[Tuple[float, float]]

Callable

Frameworks expecting callback functions of specific signatures might betype hinted usingCallable[[Arg1Type,Arg2Type],ReturnType].Examples:

fromtypingimportCallabledeffeeder(get_next_item:Callable[[],str])->None:# Bodydefasync_query(on_success:Callable[[int],None],on_error:Callable[[int,Exception],None])->None:# Body

It is possible to declare the return type of a callable withoutspecifying the call signature by substituting a literal ellipsis(three dots) for the list of arguments:

defpartial(func:Callable[...,str],*args)->Callable[...,str]:# Body

Note that there are no square brackets around the ellipsis. Thearguments of the callback are completely unconstrained in this case(and keyword arguments are acceptable).

Since using callbacks with keyword arguments is not perceived as acommon use case, there is currently no support for specifying keywordarguments withCallable. Similarly, there is no support forspecifying callback signatures with a variable number of arguments of aspecific type.

Becausetyping.Callable does double-duty as a replacement forcollections.abc.Callable,isinstance(x,typing.Callable) isimplemented by deferring toisinstance(x,collections.abc.Callable).However,isinstance(x,typing.Callable[...]) is not supported.

Generics

Since type information about objects kept in containers cannot bestatically inferred in a generic way, abstract base classes have beenextended to support subscription to denote expected types for containerelements. Example:

fromtypingimportMapping,Setdefnotify_by_email(employees:Set[Employee],overrides:Mapping[str,str])->None:...

Generics can be parameterized by using a new factory available intyping calledTypeVar. Example:

fromtypingimportSequence,TypeVarT=TypeVar('T')# Declare type variabledeffirst(l:Sequence[T])->T:# Generic functionreturnl[0]

In this case the contract is that the returned value is consistent withthe elements held by the collection.

ATypeVar() expression must always directly be assigned to avariable (it should not be used as part of a larger expression). Theargument toTypeVar() must be a string equal to the variable nameto which it is assigned. Type variables must not be redefined.

TypeVar supports constraining parametric types to a fixed set of possibletypes (note: those types cannot be parameterized by type variables). Forexample, we can define a type variable that ranges over juststr andbytes. By default, a type variable ranges over all possible types.Example of constraining a type variable:

fromtypingimportTypeVar,TextAnyStr=TypeVar('AnyStr',Text,bytes)defconcat(x:AnyStr,y:AnyStr)->AnyStr:returnx+y

The functionconcat can be called with either twostr argumentsor twobytes arguments, but not with a mix ofstr andbytesarguments.

There should be at least two constraints, if any; specifying a singleconstraint is disallowed.

Subtypes of types constrained by a type variable should be treatedas their respective explicitly listed base types in the context of thetype variable. Consider this example:

classMyStr(str):...x=concat(MyStr('apple'),MyStr('pie'))

The call is valid but the type variableAnyStr will be set tostr and notMyStr. In effect, the inferred type of the returnvalue assigned tox will also bestr.

Additionally,Any is a valid value for every type variable.Consider the following:

defcount_truthy(elements:List[Any])->int:returnsum(1foreleminelementsifelem)

This is equivalent to omitting the generic notation and just sayingelements:List.

User-defined generic types

You can include aGeneric base class to define a user-defined classas generic. Example:

fromtypingimportTypeVar,GenericfromloggingimportLoggerT=TypeVar('T')classLoggedVar(Generic[T]):def__init__(self,value:T,name:str,logger:Logger)->None:self.name=nameself.logger=loggerself.value=valuedefset(self,new:T)->None:self.log('Set '+repr(self.value))self.value=newdefget(self)->T:self.log('Get '+repr(self.value))returnself.valuedeflog(self,message:str)->None:self.logger.info('{}:{}'.format(self.name,message))

Generic[T] as a base class defines that the classLoggedVartakes a single type parameterT. This also makesT valid asa type within the class body.

TheGeneric base class uses a metaclass that defines__getitem__so thatLoggedVar[t] is valid as a type:

fromtypingimportIterabledefzero_all_vars(vars:Iterable[LoggedVar[int]])->None:forvarinvars:var.set(0)

A generic type can have any number of type variables, and type variablesmay be constrained. This is valid:

fromtypingimportTypeVar,Generic...T=TypeVar('T')S=TypeVar('S')classPair(Generic[T,S]):...

Each type variable argument toGeneric must be distinct. This isthus invalid:

fromtypingimportTypeVar,Generic...T=TypeVar('T')classPair(Generic[T,T]):# INVALID...

TheGeneric[T] base class is redundant in simple cases where yousubclass some other generic class and specify type variables for itsparameters:

fromtypingimportTypeVar,IteratorT=TypeVar('T')classMyIter(Iterator[T]):...

That class definition is equivalent to:

classMyIter(Iterator[T],Generic[T]):...

You can use multiple inheritance withGeneric:

fromtypingimportTypeVar,Generic,Sized,Iterable,Container,TupleT=TypeVar('T')classLinkedList(Sized,Generic[T]):...K=TypeVar('K')V=TypeVar('V')classMyMapping(Iterable[Tuple[K,V]],Container[Tuple[K,V]],Generic[K,V]):...

Subclassing a generic class without specifying type parameters assumesAny for each position. In the following example,MyIterableis not generic but implicitly inherits fromIterable[Any]:

fromtypingimportIterableclassMyIterable(Iterable):# Same as Iterable[Any]...

Generic metaclasses are not supported.

Scoping rules for type variables

Type variables follow normal name resolution rules.However, there are some special cases in the static typechecking context:

  • A type variable used in a generic function could be inferred to representdifferent types in the same code block. Example:
    fromtypingimportTypeVar,GenericT=TypeVar('T')deffun_1(x:T)->T:...# T heredeffun_2(x:T)->T:...# and here could be differentfun_1(1)# This is OK, T is inferred to be intfun_2('a')# This is also OK, now T is str
  • A type variable used in a method of a generic class that coincideswith one of the variables that parameterize this class is always boundto that variable. Example:
    fromtypingimportTypeVar,GenericT=TypeVar('T')classMyClass(Generic[T]):defmeth_1(self,x:T)->T:...# T heredefmeth_2(self,x:T)->T:...# and here are always the samea=MyClass()# type: MyClass[int]a.meth_1(1)# OKa.meth_2('a')# This is an error!
  • A type variable used in a method that does not match any of the variablesthat parameterize the class makes this method a generic function in thatvariable:
    T=TypeVar('T')S=TypeVar('S')classFoo(Generic[T]):defmethod(self,x:T,y:S)->S:...x=Foo()# type: Foo[int]y=x.method(0,"abc")# inferred type of y is str
  • Unbound type variables should not appear in the bodies of generic functions,or in the class bodies apart from method definitions:
    T=TypeVar('T')S=TypeVar('S')defa_fun(x:T)->None:# this is OKy=[]# type: List[T]# but below is an error!y=[]# type: List[S]classBar(Generic[T]):# this is also an erroran_attr=[]# type: List[S]defdo_something(x:S)->S:# this is OK though...
  • A generic class definition that appears inside a generic functionshould not use type variables that parameterize the generic function:
    fromtypingimportListdefa_fun(x:T)->None:# This is OKa_list=[]# type: List[T]...# This is however illegalclassMyGeneric(Generic[T]):...
  • A generic class nested in another generic class cannot use same typevariables. The scope of the type variables of the outer classdoesn’t cover the inner one:
    T=TypeVar('T')S=TypeVar('S')classOuter(Generic[T]):classBad(Iterable[T]):# Error...classAlsoBad:x=None# type: List[T] # Also an errorclassInner(Iterable[S]):# OK...attr=None# type: Inner[T] # Also OK

Instantiating generic classes and type erasure

User-defined generic classes can be instantiated. Suppose we writeaNode class inheriting fromGeneric[T]:

fromtypingimportTypeVar,GenericT=TypeVar('T')classNode(Generic[T]):...

To createNode instances you callNode() just as for a regularclass. At runtime the type (class) of the instance will beNode.But what type does it have to the type checker? The answer depends onhow much information is available in the call. If the constructor(__init__ or__new__) usesT in its signature, and acorresponding argument value is passed, the type of the correspondingargument(s) is substituted. Otherwise,Any is assumed. Example:

fromtypingimportTypeVar,GenericT=TypeVar('T')classNode(Generic[T]):x=None# type: T # Instance attribute (see below)def__init__(self,label:T=None)->None:...x=Node('')# Inferred type is Node[str]y=Node(0)# Inferred type is Node[int]z=Node()# Inferred type is Node[Any]

In case the inferred type uses[Any] but the intended type is morespecific, you can use a type comment (see below) to force the type ofthe variable, e.g.:

# (continued from previous example)a=Node()# type: Node[int]b=Node()# type: Node[str]

Alternatively, you can instantiate a specific concrete type, e.g.:

# (continued from previous example)p=Node[int]()q=Node[str]()r=Node[int]('')# Errors=Node[str](0)# Error

Note that the runtime type (class) ofp andq is still justNodeNode[int] andNode[str] are distinguishable class objects, butthe runtime class of the objects created by instantiating them doesn’trecord the distinction. This behavior is called “type erasure”; it iscommon practice in languages with generics (e.g. Java, TypeScript).

Using generic classes (parameterized or not) to access attributes will resultin type check failure. Outside the class definition body, a class attributecannot be assigned, and can only be looked up by accessing it through aclass instance that does not have an instance attribute with the same name:

# (continued from previous example)Node[int].x=1# ErrorNode[int].x# ErrorNode.x=1# ErrorNode.x# Errortype(p).x# Errorp.x# Ok (evaluates to None)Node[int]().x# Ok (evaluates to None)p.x=1# Ok, but assigning to instance attribute

Generic versions of abstract collections likeMapping orSequenceand generic versions of built-in classes –List,Dict,Set,andFrozenSet – cannot be instantiated. However, concrete user-definedsubclasses thereof and generic versions of concrete collections can beinstantiated:

data=DefaultDict[int,bytes]()

Note that one should not confuse static types and runtime classes.The type is still erased in this case and the above expression isjust a shorthand for:

data=collections.defaultdict()# type: DefaultDict[int, bytes]

It is not recommended to use the subscripted class (e.g.Node[int])directly in an expression – using a type alias (e.g.IntNode=Node[int])instead is preferred. (First, creating the subscripted class,e.g.Node[int], has a runtime cost. Second, using a type aliasis more readable.)

Arbitrary generic types as base classes

Generic[T] is only valid as a base class – it’s not a proper type.However, user-defined generic types such asLinkedList[T] from theabove example and built-in generic types and ABCs such asList[T]andIterable[T] are valid both as types and as base classes. Forexample, we can define a subclass ofDict that specializes typearguments:

fromtypingimportDict,List,OptionalclassNode:...classSymbolTable(Dict[str,List[Node]]):defpush(self,name:str,node:Node)->None:self.setdefault(name,[]).append(node)defpop(self,name:str)->Node:returnself[name].pop()deflookup(self,name:str)->Optional[Node]:nodes=self.get(name)ifnodes:returnnodes[-1]returnNone

SymbolTable is a subclass ofdict and a subtype ofDict[str,List[Node]].

If a generic base class has a type variable as a type argument, thismakes the defined class generic. For example, we can define a genericLinkedList class that is iterable and a container:

fromtypingimportTypeVar,Iterable,ContainerT=TypeVar('T')classLinkedList(Iterable[T],Container[T]):...

NowLinkedList[int] is a valid type. Note that we can useTmultiple times in the base class list, as long as we don’t use thesame type variableT multiple times withinGeneric[...].

Also consider the following example:

fromtypingimportTypeVar,MappingT=TypeVar('T')classMyDict(Mapping[str,T]):...

In this case MyDict has a single parameter, T.

Abstract generic types

The metaclass used byGeneric is a subclass ofabc.ABCMeta.A generic class can be an ABC by including abstract methodsor properties, and generic classes can also have ABCs as baseclasses without a metaclass conflict.

Type variables with an upper bound

A type variable may specify an upper bound usingbound=<type> (note:<type> itself cannot be parameterized by type variables). This means that anactual type substituted (explicitly or implicitly) for the type variable mustbe a subtype of the boundary type. Example:

fromtypingimportTypeVar,SizedST=TypeVar('ST',bound=Sized)deflonger(x:ST,y:ST)->ST:iflen(x)>len(y):returnxelse:returnylonger([1],[1,2])# ok, return type List[int]longer({1},{1,2})# ok, return type Set[int]longer([1],{1,2})# ok, return type Collection[int]

An upper bound cannot be combined with type constraints (as in usedAnyStr, see the example earlier); type constraints cause theinferred type to be _exactly_ one of the constraint types, while anupper bound just requires that the actual type is a subtype of theboundary type.

Covariance and contravariance

Consider a classEmployee with a subclassManager. Nowsuppose we have a function with an argument annotated withList[Employee]. Should we be allowed to call this function with avariable of typeList[Manager] as its argument? Many people wouldanswer “yes, of course” without even considering the consequences.But unless we know more about the function, a type checker shouldreject such a call: the function might append anEmployee instanceto the list, which would violate the variable’s type in the caller.

It turns out such an argument actscontravariantly, whereas theintuitive answer (which is correct in case the function doesn’t mutateits argument!) requires the argument to actcovariantly. A longerintroduction to these concepts can be found onWikipedia and inPEP 483; here we just show how to controla type checker’s behavior.

By default generic types are consideredinvariant in all type variables,which means that values for variables annotated with types likeList[Employee] must exactly match the type annotation – no subclasses orsuperclasses of the type parameter (in this exampleEmployee) areallowed.

To facilitate the declaration of container types where covariant orcontravariant type checking is acceptable, type variables accept keywordargumentscovariant=True orcontravariant=True. At most one of thesemay be passed. Generic types defined with such variables are consideredcovariant or contravariant in the corresponding variable. By convention,it is recommended to use names ending in_co for type variablesdefined withcovariant=True and names ending in_contra for thatdefined withcontravariant=True.

A typical example involves defining an immutable (or read-only)container class:

fromtypingimportTypeVar,Generic,Iterable,IteratorT_co=TypeVar('T_co',covariant=True)classImmutableList(Generic[T_co]):def__init__(self,items:Iterable[T_co])->None:...def__iter__(self)->Iterator[T_co]:......classEmployee:...classManager(Employee):...defdump_employees(emps:ImmutableList[Employee])->None:forempinemps:...mgrs=ImmutableList([Manager()])# type: ImmutableList[Manager]dump_employees(mgrs)# OK

The read-only collection classes intyping are all declaredcovariant in their type variable (e.g.Mapping andSequence). Themutable collection classes (e.g.MutableMapping andMutableSequence) are declared invariant. The one example ofa contravariant type is theGenerator type, which is contravariantin thesend() argument type (see below).

Note: Covariance or contravariance isnot a property of a type variable,but a property of a generic class defined using this variable.Variance is only applicable to generic types; generic functionsdo not have this property. The latter should be defined using onlytype variables withoutcovariant orcontravariant keyword arguments.For example, the following example isfine:

fromtypingimportTypeVarclassEmployee:...classManager(Employee):...E=TypeVar('E',bound=Employee)defdump_employee(e:E)->None:...dump_employee(Manager())# OK

while the following is prohibited:

B_co=TypeVar('B_co',covariant=True)defbad_func(x:B_co)->B_co:# Flagged as error by a type checker...

The numeric tower

PEP 3141 defines Python’s numeric tower, and the stdlib modulenumbers implements the corresponding ABCs (Number,Complex,Real,Rational andIntegral). There are someissues with these ABCs, but the built-in concrete numeric classescomplex,float andint are ubiquitous (especially thelatter two :-).

Rather than requiring that users writeimportnumbers and then usenumbers.Float etc., this PEP proposes a straightforward shortcutthat is almost as effective: when an argument is annotated as havingtypefloat, an argument of typeint is acceptable; similar,for an argument annotated as having typecomplex, arguments oftypefloat orint are acceptable. This does not handleclasses implementing the corresponding ABCs or thefractions.Fraction class, but we believe those use cases areexceedingly rare.

Forward references

When a type hint contains names that have not been defined yet, thatdefinition may be expressed as a string literal, to be resolved later.

A situation where this occurs commonly is the definition of acontainer class, where the class being defined occurs in the signatureof some of the methods. For example, the following code (the start ofa simple binary tree implementation) does not work:

classTree:def__init__(self,left:Tree,right:Tree):self.left=leftself.right=right

To address this, we write:

classTree:def__init__(self,left:'Tree',right:'Tree'):self.left=leftself.right=right

The string literal should contain a valid Python expression (i.e.,compile(lit,'','eval') should be a valid code object) and itshould evaluate without errors once the module has been fully loaded.The local and global namespace in which it is evaluated should be thesame namespaces in which default arguments to the same function wouldbe evaluated.

Moreover, the expression should be parseable as a valid type hint, i.e.,it is constrained by the rules from the sectionAcceptable type hintsabove.

It is allowable to use string literals aspart of a type hint, forexample:

classTree:...defleaves(self)->List['Tree']:...

A common use for forward references is when e.g. Django models areneeded in the signatures. Typically, each model is in a separatefile, and has methods taking arguments whose type involves other models.Because of the way circular imports work in Python, it is often notpossible to import all the needed models directly:

# File models/a.pyfrommodels.bimportBclassA(Model):deffoo(self,b:B):...# File models/b.pyfrommodels.aimportAclassB(Model):defbar(self,a:A):...# File main.pyfrommodels.aimportAfrommodels.bimportB

Assuming main is imported first, this will fail with an ImportError atthe linefrommodels.aimportA in models/b.py, which is beingimported from models/a.py before a has defined class A. The solutionis to switch to module-only imports and reference the models by their_module_._class_ name:

# File models/a.pyfrommodelsimportbclassA(Model):deffoo(self,b:'b.B'):...# File models/b.pyfrommodelsimportaclassB(Model):defbar(self,a:'a.A'):...# File main.pyfrommodels.aimportAfrommodels.bimportB

Union types

Since accepting a small, limited set of expected types for a singleargument is common, there is a new special factory calledUnion.Example:

fromtypingimportUniondefhandle_employees(e:Union[Employee,Sequence[Employee]])->None:ifisinstance(e,Employee):e=[e]...

A type factored byUnion[T1,T2,...] is a supertypeof all typesT1,T2, etc., so that a value thatis a member of one of these types is acceptable for an argumentannotated byUnion[T1,T2,...].

One common case of union types areoptional types. By default,None is an invalid value for any type, unless a default value ofNone has been provided in the function definition. Examples:

defhandle_employee(e:Union[Employee,None])->None:...

As a shorthand forUnion[T1,None] you can writeOptional[T1];for example, the above is equivalent to:

fromtypingimportOptionaldefhandle_employee(e:Optional[Employee])->None:...

A past version of this PEP allowed type checkers to assume an optionaltype when the default value isNone, as in this code:

defhandle_employee(e:Employee=None):...

This would have been treated as equivalent to:

defhandle_employee(e:Optional[Employee]=None)->None:...

This is no longer the recommended behavior. Type checkers should movetowards requiring the optional type to be made explicit.

Support for singleton types in unions

A singleton instance is frequently used to mark some special condition,in particular in situations whereNone is also a valid valuefor a variable. Example:

_empty=object()deffunc(x=_empty):ifxis_empty:# default argument valuereturn0elifxisNone:# argument was provided and it's Nonereturn1else:returnx*2

To allow precise typing in such situations, the user should usetheUnion type in conjunction with theenum.Enum class providedby the standard library, so that type errors can be caught statically:

fromtypingimportUnionfromenumimportEnumclassEmpty(Enum):token=0_empty=Empty.tokendeffunc(x:Union[int,None,Empty]=_empty)->int:boom=x*42# This fails type checkifxis_empty:return0elifxisNone:return1else:# At this point typechecker knows that x can only have type intreturnx*2

Since the subclasses ofEnum cannot be further subclassed,the type of variablex can be statically inferred in all branchesof the above example. The same approach is applicable if more than onesingleton object is needed: one can use an enumeration that has more thanone value:

classReason(Enum):timeout=1error=2defprocess(response:Union[str,Reason]='')->str:ifresponseisReason.timeout:return'TIMEOUT'elifresponseisReason.error:return'ERROR'else:# response can be only str, all other possible values exhaustedreturn'PROCESSED: '+response

TheAny type

A special kind of type isAny. Every type is consistent withAny. It can be considered a type that has all values and all methods.Note thatAny and builtin typeobject are completely different.

When the type of a value isobject, the type checker will rejectalmost all operations on it, and assigning it to a variable (or usingit as a return value) of a more specialized type is a type error. Onthe other hand, when a value has typeAny, the type checker willallow all operations on it, and a value of typeAny can be assignedto a variable (or used as a return value) of a more constrained type.

A function parameter without an annotation is assumed to be annotated withAny. If a generic type is used without specifying type parameters,they are assumed to beAny:

fromtypingimportMappingdefuse_map(m:Mapping)->None:# Same as Mapping[Any, Any]...

This rule also applies toTuple, in annotation context it is equivalenttoTuple[Any,...] and, in turn, totuple. As well, a bareCallable in an annotation is equivalent toCallable[...,Any] and,in turn, tocollections.abc.Callable:

fromtypingimportTuple,List,Callabledefcheck_args(args:Tuple)->bool:...check_args(())# OKcheck_args((42,'abc'))# Also OKcheck_args(3.14)# Flagged as error by a type checker# A list of arbitrary callables is accepted by this functiondefapply_callbacks(cbs:List[Callable])->None:...

TheNoReturn type

Thetyping module provides a special typeNoReturn to annotate functionsthat never return normally. For example, a function that unconditionallyraises an exception:

fromtypingimportNoReturndefstop()->NoReturn:raiseRuntimeError('no way')

TheNoReturn annotation is used for functions such assys.exit.Static type checkers will ensure that functions annotated as returningNoReturn truly never return, either implicitly or explicitly:

importsysfromtypingimportNoReturndeff(x:int)->NoReturn:# Error, f(0) implicitly returns Noneifx!=0:sys.exit(1)

The checkers will also recognize that the code after calls to such functionsis unreachable and will behave accordingly:

# continue from first exampledefg(x:int)->int:ifx>0:returnxstop()return'whatever works'# Error might be not reported by some checkers# that ignore errors in unreachable blocks

TheNoReturn type is only valid as a return annotation of functions,and considered an error if it appears in other positions:

fromtypingimportList,NoReturn# All of the following are errorsdefbad1(x:NoReturn)->int:...bad2=None# type: NoReturndefbad3()->List[NoReturn]:...

The type of class objects

Sometimes you want to talk about class objects, in particular classobjects that inherit from a given class. This can be spelled asType[C] whereC is a class. To clarify: whileC (whenused as an annotation) refers to instances of classC,Type[C]refers tosubclasses ofC. (This is a similar distinction asbetweenobject andtype.)

For example, suppose we have the following classes:

classUser:...# Abstract base for User classesclassBasicUser(User):...classProUser(User):...classTeamUser(User):...

And suppose we have a function that creates an instance of one ofthese classes if you pass it a class object:

defnew_user(user_class):user=user_class()# (Here we could write the user object to a database)returnuser

WithoutType[] the best we could do to annotatenew_user()would be:

defnew_user(user_class:type)->User:...

However usingType[] and a type variable with an upper bound wecan do much better:

U=TypeVar('U',bound=User)defnew_user(user_class:Type[U])->U:...

Now when we callnew_user() with a specific subclass ofUser atype checker will infer the correct type of the result:

joe=new_user(BasicUser)# Inferred type is BasicUser

The value corresponding toType[C] must be an actual class objectthat’s a subtype ofC, not a special form. In other words, in theabove example calling e.g.new_user(Union[BasicUser,ProUser]) isrejected by the type checker (in addition to failing at runtimebecause you can’t instantiate a union).

Note that it is legal to use a union of classes as the parameter forType[], as in:

defnew_non_team_user(user_class:Type[Union[BasicUser,ProUser]]):user=new_user(user_class)...

However the actual argument passed in at runtime must still be aconcrete class object, e.g. in the above example:

new_non_team_user(ProUser)# OKnew_non_team_user(TeamUser)# Disallowed by type checker

Type[Any] is also supported (see below for its meaning).

Type[T] whereT is a type variable is allowed when annotating thefirst argument of a class method (see the relevant section).

Any other special constructs likeTuple orCallable are not allowedas an argument toType.

There are some concerns with this feature: for example whennew_user() callsuser_class() this implies that all subclassesofUser must support this in their constructor signature. Howeverthis is not unique toType[]: class methods have similar concerns.A type checker ought to flag violations of such assumptions, but bydefault constructor calls that match the constructor signature in theindicated base class (User in the example above) should beallowed. A program containing a complex or extensible class hierarchymight also handle this by using a factory class method. A futurerevision of this PEP may introduce better ways of dealing with theseconcerns.

WhenType is parameterized it requires exactly one parameter.PlainType without brackets is equivalent toType[Any] andthis in turn is equivalent totype (the root of Python’s metaclasshierarchy). This equivalence also motivates the name,Type, asopposed to alternatives likeClass orSubType, which wereproposed while this feature was under discussion; this is similar tothe relationship between e.g.List andlist.

Regarding the behavior ofType[Any] (orType ortype),accessing attributes of a variable with this type only providesattributes and methods defined bytype (for example,__repr__() and__mro__). Such a variable can be called witharbitrary arguments, and the return type isAny.

Type is covariant in its parameter, becauseType[Derived] is asubtype ofType[Base]:

defnew_pro_user(pro_user_class:Type[ProUser]):user=new_user(pro_user_class)# OK...

Annotating instance and class methods

In most cases the first argument of class and instance methodsdoes not need to be annotated, and it is assumed to have thetype of the containing class for instance methods, and a type objecttype corresponding to the containing class object for class methods.In addition, the first argument in an instance method can be annotatedwith a type variable. In this case the return type may use the sametype variable, thus making that method a generic function. For example:

T=TypeVar('T',bound='Copyable')classCopyable:defcopy(self:T)->T:# return a copy of selfclassC(Copyable):...c=C()c2=c.copy()# type here should be C

The same applies to class methods usingType[] in an annotationof the first argument:

T=TypeVar('T',bound='C')classC:@classmethoddeffactory(cls:Type[T])->T:# make a new instance of clsclassD(C):...d=D.factory()# type here should be D

Note that some type checkers may apply restrictions on this use, such asrequiring an appropriate upper bound for the type variable used(see examples).

Version and platform checking

Type checkers are expected to understand simple version and platformchecks, e.g.:

importsysifsys.version_info[0]>=3:# Python 3 specific definitionselse:# Python 2 specific definitionsifsys.platform=='win32':# Windows specific definitionselse:# Posix specific definitions

Don’t expect a checker to understand obfuscations like"".join(reversed(sys.platform))=="xunil".

Runtime or type checking?

Sometimes there’s code that must be seen by a type checker (or otherstatic analysis tools) but should not be executed. For suchsituations thetyping module defines a constant,TYPE_CHECKING, that is consideredTrue during type checking(or other static analysis) butFalse at runtime. Example:

importtypingiftyping.TYPE_CHECKING:importexpensive_moddefa_func(arg:'expensive_mod.SomeClass')->None:a_var=arg# type: expensive_mod.SomeClass...

(Note that the type annotation must be enclosed in quotes, making it a“forward reference”, to hide theexpensive_mod reference from theinterpreter runtime. In the#type comment no quotes are needed.)

This approach may also be useful to handle import cycles.

Arbitrary argument lists and default argument values

Arbitrary argument lists can as well be type annotated,so that the definition:

deffoo(*args:str,**kwds:int):...

is acceptable and it means that, e.g., all of the followingrepresent function calls with valid types of arguments:

foo('a','b','c')foo(x=1,y=2)foo('',z=0)

In the body of functionfoo, the type of variableargs isdeduced asTuple[str,...] and the type of variablekwdsisDict[str,int].

In stubs it may be useful to declare an argument as having a defaultwithout specifying the actual default value. For example:

deffoo(x:AnyStr,y:AnyStr=...)->AnyStr:...

What should the default value look like? Any of the options"",b"" orNone fails to satisfy the type constraint.

In such cases the default value may be specified as a literalellipsis, i.e. the above example is literally what you would write.

Positional-only arguments

Some functions are designed to take their arguments only positionally,and expect their callers never to use the argument’s name to providethat argument by keyword. All arguments with names beginning with__ are assumed to be positional-only, except if their names alsoend with__:

defquux(__x:int,__y__:int=0)->None:...quux(3,__y__=1)# This call is fine.quux(__x=3)# This call is an error.

Annotating generator functions and coroutines

The return type of generator functions can be annotated bythe generic typeGenerator[yield_type,send_type,return_type] provided bytyping.py module:

defecho_round()->Generator[int,float,str]:res=yieldwhileres:res=yieldround(res)return'OK'

Coroutines introduced inPEP 492 are annotated with the same syntax asordinary functions. However, the return type annotation corresponds to thetype ofawait expression, not to the coroutine type:

asyncdefspam(ignored:int)->str:return'spam'asyncdeffoo()->None:bar=awaitspam(42)# type: str

Thetyping.py module provides a generic version of ABCcollections.abc.Coroutine to specify awaitables that also supportsend() andthrow() methods. The variance and order of type variablescorrespond to those ofGenerator, namelyCoroutine[T_co,T_contra,V_co],for example:

fromtypingimportList,Coroutinec=None# type: Coroutine[List[str], str, int]...x=c.send('hi')# type: List[str]asyncdefbar()->None:x=awaitc# type: int

The module also provides generic ABCsAwaitable,AsyncIterable, andAsyncIterator for situations where more precisetypes cannot be specified:

defop()->typing.Awaitable[str]:ifcond:returnspam(42)else:returnasyncio.Future(...)

Compatibility with other uses of function annotations

A number of existing or potential use cases for function annotationsexist, which are incompatible with type hinting. These may confusea static type checker. However, since type hinting annotations have noruntime behavior (other than evaluation of the annotation expression andstoring annotations in the__annotations__ attribute of the functionobject), this does not make the program incorrect – it just may causea type checker to emit spurious warnings or errors.

To mark portions of the program that should not be covered by typehinting, you can use one or more of the following:

  • a#type:ignore comment;
  • a@no_type_check decorator on a class or function;
  • a custom class or function decorator marked with@no_type_check_decorator.

For more details see later sections.

In order for maximal compatibility with offline type checking it mayeventually be a good idea to change interfaces that rely on annotationsto switch to a different mechanism, for example a decorator. In Python3.5 there is no pressure to do this, however. See also the longerdiscussion underRejected alternatives below.

Type comments

No first-class syntax support for explicitly marking variables as beingof a specific type is added by this PEP. To help with type inference incomplex cases, a comment of the following format may be used:

x=[]# type: List[Employee]x,y,z=[],[],[]# type: List[int], List[int], List[str]x,y,z=[],[],[]# type: (List[int], List[int], List[str])a,b,*c=range(5)# type: float, float, List[float]x=[1,2]# type: List[int]

Type comments should be put on the last line of the statement thatcontains the variable definition. They can also be placed onwith statements andfor statements, right after the colon.

Examples of type comments onwith andfor statements:

withfrobnicate()asfoo:# type: int# Here foo is an int...forx,yinpoints:# type: float, float# Here x and y are floats...

In stubs it may be useful to declare the existence of a variablewithout giving it an initial value. This can be done usingPEP 526variable annotation syntax:

fromtypingimportIOstream:IO[str]

The above syntax is acceptable in stubs for all versions of Python.However, in non-stub code for versions of Python 3.5 and earlierthere is a special case:

fromtypingimportIOstream=None# type: IO[str]

Type checkers should not complain about this (despite the valueNone not matching the given type), nor should they change theinferred type toOptional[...] (despite the rule that does thisfor annotated arguments with a default value ofNone). Theassumption here is that other code will ensure that the variable isgiven a value of the proper type, and all uses can assume that thevariable has the given type.

The#type:ignore comment should be put on the line that theerror refers to:

importhttp.clienterrors={'not_found':http.client.NOT_FOUND# type: ignore}

A#type:ignore comment on a line by itself at the top of a file,before any docstrings, imports, or other executable code, silences allerrors in the file. Blank lines and other comments, such as shebanglines and coding cookies, may precede the#type:ignore comment.

In some cases, linting tools or other comments may be needed on the sameline as a type comment. In these cases, the type comment should be beforeother comments and linting markers:

# type: ignore # <comment or other marker>

If type hinting proves useful in general, a syntax for typing variablesmay be provided in a future Python version. (UPDATE: This syntaxwas added in Python 3.6 throughPEP 526.)

Casts

Occasionally the type checker may need a different kind of hint: theprogrammer may know that an expression is of a more constrained typethan a type checker may be able to infer. For example:

fromtypingimportList,castdeffind_first_str(a:List[object])->str:index=next(ifori,xinenumerate(a)ifisinstance(x,str))# We only get here if there's at least one string in areturncast(str,a[index])

Some type checkers may not be able to infer that the type ofa[index] isstr and only inferobject orAny, but weknow that (if the code gets to that point) it must be a string. Thecast(t,x) call tells the type checker that we are confident thatthe type ofx ist. At runtime a cast always returns theexpression unchanged – it does not check the type, and it does notconvert or coerce the value.

Casts differ from type comments (see the previous section). When usinga type comment, the type checker should still verify that the inferredtype is consistent with the stated type. When using a cast, the typechecker should blindly believe the programmer. Also, casts can be usedin expressions, while type comments only apply to assignments.

NewType helper function

There are also situations where a programmer might want to avoid logicalerrors by creating simple classes. For example:

classUserId(int):passdefget_by_user_id(user_id:UserId):...

However, this approach introduces a runtime overhead. To avoid this,typing.py provides a helper functionNewType that createssimple unique types with almost zero runtime overhead. For a static typecheckerDerived=NewType('Derived',Base) is roughly equivalentto a definition:

classDerived(Base):def__init__(self,_x:Base)->None:...

While at runtime,NewType('Derived',Base) returns a dummy functionthat simply returns its argument. Type checkers require explicit castsfromint whereUserId is expected, while implicitly castingfromUserId whereint is expected. Examples:

UserId=NewType('UserId',int)defname_by_id(user_id:UserId)->str:...UserId('user')# Fails type checkname_by_id(42)# Fails type checkname_by_id(UserId(42))# OKnum=UserId(5)+1# type: int

NewType accepts exactly two arguments: a name for the new unique type,and a base class. The latter should be a proper class (i.e.,not a type construct likeUnion, etc.), or another unique type createdby callingNewType. The function returned byNewTypeaccepts only one argument; this is equivalent to supporting only oneconstructor accepting an instance of the base class (see above). Example:

classPacketId:def__init__(self,major:int,minor:int)->None:self._major=majorself._minor=minorTcpPacketId=NewType('TcpPacketId',PacketId)packet=PacketId(100,100)tcp_packet=TcpPacketId(packet)# OKtcp_packet=TcpPacketId(127,0)# Fails in type checker and at runtime

Bothisinstance andissubclass, as well as subclassing will failforNewType('Derived',Base) since function objects don’t supportthese operations.

Stub Files

Stub files are files containing type hints that are only for use bythe type checker, not at runtime. There are several use cases forstub files:

  • Extension modules
  • Third-party modules whose authors have not yet added type hints
  • Standard library modules for which type hints have not yet beenwritten
  • Modules that must be compatible with Python 2 and 3
  • Modules that use annotations for other purposes

Stub files have the same syntax as regular Python modules. There is onefeature of thetyping module that is different in stub files:the@overload decorator described below.

The type checker should only check function signatures in stub files;It is recommended that function bodies in stub files just be a singleellipsis (...).

The type checker should have a configurable search path for stub files.If a stub file is found the type checker should not read thecorresponding “real” module.

While stub files are syntactically valid Python modules, they use the.pyi extension to make it possible to maintain stub files in thesame directory as the corresponding real module. This also reinforcesthe notion that no runtime behavior should be expected of stub files.

Additional notes on stub files:

  • Modules and variables imported into the stub are not consideredexported from the stub unless the import uses theimport...as... form or the equivalentfrom...import...as... form.(UPDATE: To clarify, the intention here is that only namesimported using the formXasX will be exported, i.e. the namebefore and afteras must be the same.)
  • However, as an exception to the previous bullet, all objectsimported into a stub usingfrom...import* are consideredexported. (This makes it easier to re-export all objects from agiven module that may vary by Python version.)
  • Just like innormal Python files, submodulesautomatically become exported attributes of their parent modulewhen imported. For example, if thespam package has thefollowing directory structure:
    spam/__init__.pyiham.pyi

    where__init__.pyi contains a line such asfrom.importhamorfrom.hamimportHam, thenham is an exported attributeofspam.

  • Stub files may be incomplete. To make type checkers aware of this, the filecan contain the following code:
    def__getattr__(name)->Any:...

    Any identifier not defined in the stub is therefore assumed to be of typeAny.

Function/method overloading

The@overload decorator allows describing functions and methodsthat support multiple different combinations of argument types. Thispattern is used frequently in builtin modules and types. For example,the__getitem__() method of thebytes type can be described asfollows:

fromtypingimportoverloadclassbytes:...@overloaddef__getitem__(self,i:int)->int:...@overloaddef__getitem__(self,s:slice)->bytes:...

This description is more precise than would be possible using unions(which cannot express the relationship between the argument and returntypes):

fromtypingimportUnionclassbytes:...def__getitem__(self,a:Union[int,slice])->Union[int,bytes]:...

Another example where@overload comes in handy is the type of thebuiltinmap() function, which takes a different number ofarguments depending on the type of the callable:

fromtypingimportCallable,Iterable,Iterator,Tuple,TypeVar,overloadT1=TypeVar('T1')T2=TypeVar('T2')S=TypeVar('S')@overloaddefmap(func:Callable[[T1],S],iter1:Iterable[T1])->Iterator[S]:...@overloaddefmap(func:Callable[[T1,T2],S],iter1:Iterable[T1],iter2:Iterable[T2])->Iterator[S]:...# ... and we could add more items to support more than two iterables

Note that we could also easily add items to supportmap(None,...):

@overloaddefmap(func:None,iter1:Iterable[T1])->Iterable[T1]:...@overloaddefmap(func:None,iter1:Iterable[T1],iter2:Iterable[T2])->Iterable[Tuple[T1,T2]]:...

Uses of the@overload decorator as shown above are suitable forstub files. In regular modules, a series of@overload-decorateddefinitions must be followed by exactly onenon-@overload-decorated definition (for the same function/method).The@overload-decorated definitions are for the benefit of thetype checker only, since they will be overwritten by thenon-@overload-decorated definition, while the latter is used atruntime but should be ignored by a type checker. At runtime, callinga@overload-decorated function directly will raiseNotImplementedError. Here’s an example of a non-stub overloadthat can’t easily be expressed using a union or a type variable:

@overloaddefutf8(value:None)->None:pass@overloaddefutf8(value:bytes)->bytes:pass@overloaddefutf8(value:unicode)->bytes:passdefutf8(value):<actualimplementation>

NOTE: While it would be possible to provide a multiple dispatchimplementation using this syntax, its implementation would requireusingsys._getframe(), which is frowned upon. Also, designing andimplementing an efficient multiple dispatch mechanism is hard, whichis why previous attempts were abandoned in favor offunctools.singledispatch(). (SeePEP 443, especially its section“Alternative approaches”.) In the future we may come up with asatisfactory multiple dispatch design, but we don’t want such a designto be constrained by the overloading syntax defined for type hints instub files. It is also possible that both features will developindependent from each other (since overloading in the type checkerhas different use cases and requirements than multiple dispatchat runtime – e.g. the latter is unlikely to support generic types).

A constrainedTypeVar type can often be used instead of using the@overload decorator. For example, the definitions ofconcat1andconcat2 in this stub file are equivalent:

fromtypingimportTypeVar,TextAnyStr=TypeVar('AnyStr',Text,bytes)defconcat1(x:AnyStr,y:AnyStr)->AnyStr:...@overloaddefconcat2(x:str,y:str)->str:...@overloaddefconcat2(x:bytes,y:bytes)->bytes:...

Some functions, such asmap orbytes.__getitem__ above, can’tbe represented precisely using type variables. However, unlike@overload, type variables can also be used outside stub files. Werecommend that@overload is only used in cases where a typevariable is not sufficient, due to its special stub-only status.

Another important difference between type variables such asAnyStrand using@overload is that the prior can also be used to defineconstraints for generic class type parameters. For example, the typeparameter of the generic classtyping.IO is constrained (onlyIO[str],IO[bytes] andIO[Any] are valid):

classIO(Generic[AnyStr]):...

Storing and distributing stub files

The easiest form of stub file storage and distribution is to put themalongside Python modules in the same directory. This makes them easy tofind by both programmers and the tools. However, since packagemaintainers are free not to add type hinting to their packages,third-party stubs installable bypip from PyPI are also supported.In this case we have to consider three issues: naming, versioning,installation path.

This PEP does not provide a recommendation on a naming scheme thatshould be used for third-party stub file packages. Discoverability willhopefully be based on package popularity, like with Django packages forexample.

Third-party stubs have to be versioned using the lowest version of thesource package that is compatible. Example: FooPackage has versions1.0, 1.1, 1.2, 1.3, 2.0, 2.1, 2.2. There are API changes in versions1.1, 2.0 and 2.2. The stub file package maintainer is free to releasestubs for all versions but at least 1.0, 1.1, 2.0 and 2.2 are neededto enable the end user type check all versions. This is because theuser knows that the closestlower or equal version of stubs iscompatible. In the provided example, for FooPackage 1.3 the user wouldchoose stubs version 1.1.

Note that if the user decides to use the “latest” available sourcepackage, using the “latest” stub files should generally also work ifthey’re updated often.

Third-party stub packages can use any location for stub storage. Typecheckers should search for them using PYTHONPATH. A default fallbackdirectory that is always checked isshared/typehints/pythonX.Y/ (forsome PythonX.Y as determined by the type checker, not just the installedversion). Since there can only be one package installed for a given Pythonversion per environment, no additional versioning is performed under thatdirectory (just like bare directory installs bypip in site-packages).Stub file package authors might use the following snippet insetup.py:

...data_files=[('shared/typehints/python{}.{}'.format(*sys.version_info[:2]),pathlib.Path(SRC_PATH).glob('**/*.pyi'),),],...

(UPDATE: As of June 2018 the recommended way to distribute typehints for third-party packages has changed – in addition to typeshed(see the next section) there is now a standard for distributing typehints,PEP 561. It supports separately installable packages containingstubs, stub files included in the same distribution as the executablecode of a package, and inline type hints, the latter two optionsenabled by including a file namedpy.typed in the package.)

The Typeshed Repo

There is ashared repository where useful stubs are beingcollected. Policies regarding the stubs collected here will bedecided separately and reported in the repo’s documentation.Note that stubs for a given package will not be included hereif the package owners have specifically requested that they be omitted.

Exceptions

No syntax for listing explicitly raised exceptions is proposed.Currently the only known use case for this feature is documentational,in which case the recommendation is to put this information in adocstring.

Thetyping Module

To open the usage of static type checking to Python 3.5 as well as olderversions, a uniform namespace is required. For this purpose, a newmodule in the standard library is introduced calledtyping.

It defines the fundamental building blocks for constructing types(e.g.Any), types representing generic variants of builtincollections (e.g.List), types representing genericcollection ABCs (e.g.Sequence), and a small collection ofconvenience definitions.

Note that special type constructs, such asAny,Union,and type variables defined usingTypeVar are only supportedin the type annotation context, andGeneric may only be usedas a base class. All of these (except for unparameterized generics)will raiseTypeError if appear inisinstance orissubclass.

Fundamental building blocks:

  • Any, used asdefget(key:str)->Any:...
  • Union, used asUnion[Type1,Type2,Type3]
  • Callable, used asCallable[[Arg1Type,Arg2Type],ReturnType]
  • Tuple, used by listing the element types, for exampleTuple[int,int,str].The empty tuple can be typed asTuple[()].Arbitrary-length homogeneous tuples can be expressedusing one type and ellipsis, for exampleTuple[int,...].(The... here are part of the syntax, a literal ellipsis.)
  • TypeVar, used asX=TypeVar('X',Type1,Type2,Type3) or simplyY=TypeVar('Y') (see above for more details)
  • Generic, used to create user-defined generic classes
  • Type, used to annotate class objects

Generic variants of builtin collections:

  • Dict, used asDict[key_type,value_type]
  • DefaultDict, used asDefaultDict[key_type,value_type],a generic variant ofcollections.defaultdict
  • List, used asList[element_type]
  • Set, used asSet[element_type]. See remark forAbstractSetbelow.
  • FrozenSet, used asFrozenSet[element_type]

Note:Dict,DefaultDict,List,Set andFrozenSetare mainly useful for annotating return values.For arguments, prefer the abstract collection types defined below,e.g.Mapping,Sequence orAbstractSet.

Generic variants of container ABCs (and a few non-containers):

  • Awaitable
  • AsyncIterable
  • AsyncIterator
  • ByteString
  • Callable (see above, listed here for completeness)
  • Collection
  • Container
  • ContextManager
  • Coroutine
  • Generator, used asGenerator[yield_type,send_type,return_type]. This represents the return value of generatorfunctions. It is a subtype ofIterable and it has additionaltype variables for the type accepted by thesend() method (itis contravariant in this variable – a generator that accepts sending itEmployee instance is valid in a context where a generator is requiredthat accepts sending itManager instances) and the return type of thegenerator.
  • Hashable (not generic, but present for completeness)
  • ItemsView
  • Iterable
  • Iterator
  • KeysView
  • Mapping
  • MappingView
  • MutableMapping
  • MutableSequence
  • MutableSet
  • Sequence
  • Set, renamed toAbstractSet. This name change was requiredbecauseSet in thetyping module meansset() withgenerics.
  • Sized (not generic, but present for completeness)
  • ValuesView

A few one-off types are defined that test for single special methods(similar toHashable orSized):

  • Reversible, to test for__reversed__
  • SupportsAbs, to test for__abs__
  • SupportsComplex, to test for__complex__
  • SupportsFloat, to test for__float__
  • SupportsInt, to test for__int__
  • SupportsRound, to test for__round__
  • SupportsBytes, to test for__bytes__

Convenience definitions:

  • Optional, defined byOptional[t]==Union[t,None]
  • Text, a simple alias forstr in Python 3, forunicode in Python 2
  • AnyStr, defined asTypeVar('AnyStr',Text,bytes)
  • NamedTuple, used asNamedTuple(type_name,[(field_name,field_type),...])and equivalent tocollections.namedtuple(type_name,[field_name,...]).This is useful to declare the types of the fields of a named tupletype.
  • NewType, used to create unique types with little runtime overheadUserId=NewType('UserId',int)
  • cast(), described earlier
  • @no_type_check, a decorator to disable type checking per class orfunction (see below)
  • @no_type_check_decorator, a decorator to create your own decoratorswith the same meaning as@no_type_check (see below)
  • @type_check_only, a decorator only available during type checkingfor use in stub files (see above); marks a class or function asunavailable during runtime
  • @overload, described earlier
  • get_type_hints(), a utility function to retrieve the type hints from afunction or method. Given a function or method object, it returnsa dict with the same format as__annotations__, but evaluatingforward references (which are given as string literals) as expressionsin the context of the original function or method definition.
  • TYPE_CHECKING,False at runtime butTrue to type checkers

I/O related types:

  • IO (generic overAnyStr)
  • BinaryIO (a simple subtype ofIO[bytes])
  • TextIO (a simple subtype ofIO[str])

Types related to regular expressions and there module:

  • Match and Pattern, types ofre.match() andre.compile()results (generic overAnyStr)

Suggested syntax for Python 2.7 and straddling code

Some tools may want to support type annotations in code that must becompatible with Python 2.7. For this purpose this PEP has a suggested(but not mandatory) extension where function annotations are placed ina#type: comment. Such a comment must be placed immediatelyfollowing the function header (before the docstring). An example: thefollowing Python 3 code:

defembezzle(self,account:str,funds:int=1000000,*fake_receipts:str)->None:"""Embezzle funds from account using fake receipts."""<codegoeshere>

is equivalent to the following:

defembezzle(self,account,funds=1000000,*fake_receipts):# type: (str, int, *str) -> None"""Embezzle funds from account using fake receipts."""<codegoeshere>

Note that for methods, no type is needed forself.

For an argument-less method it would look like this:

defload_cache(self):# type: () -> bool<code>

Sometimes you want to specify the return type for a function or methodwithout (yet) specifying the argument types. To support thisexplicitly, the argument list may be replaced with an ellipsis.Example:

defsend_email(address,sender,cc,bcc,subject,body):# type: (...) -> bool"""Send an email message.  Return True if successful."""<code>

Sometimes you have a long list of parameters and specifying theirtypes in a single#type: comment would be awkward. To this endyou may list the arguments one per line and add a#type: commentper line after an argument’s associated comma, if any.To specify the return type use the ellipsis syntax. Specifying the returntype is not mandatory and not every argument needs to be given a type.A line with a#type: comment should contain exactly one argument.The type comment for the last argument (if any) should precede the closeparenthesis. Example:

defsend_email(address,# type: Union[str, List[str]]sender,# type: strcc,# type: Optional[List[str]]bcc,# type: Optional[List[str]]subject='',body=None# type: List[str]):# type: (...) -> bool"""Send an email message.  Return True if successful."""<code>

Notes:

  • Tools that support this syntax should support it regardless of thePython version being checked. This is necessary in order to supportcode that straddles Python 2 and Python 3.
  • It is not allowed for an argument or return value to have botha type annotation and a type comment.
  • When using the short form (e.g.#type:(str,int)->None)every argument must be accounted for, except the first argument ofinstance and class methods (those are usually omitted, but it’sallowed to include them).
  • The return type is mandatory for the short form. If in Python 3 youwould omit some argument or the return type, the Python 2 notationshould useAny.
  • When using the short form, for*args and**kwds, put 1 or 2stars in front of the corresponding type annotation. (As withPython 3 annotations, the annotation here denotes the type of theindividual argument values, not of the tuple/dict that you receiveas the special argument valueargs orkwds.)
  • Like other type comments, any names used in the annotations must beimported or defined by the module containing the annotation.
  • When using the short form, the entire annotation must be one line.
  • The short form may also occur on the same line as the closeparenthesis, e.g.:
    defadd(a,b):# type: (int, int) -> intreturna+b
  • Misplaced type comments will be flagged as errors by a type checker.If necessary, such comments could be commented twice. For example:
    deff():'''Docstring'''# type: () -> None  # Error!defg():'''Docstring'''# # type: () -> None  # This is OK

When checking Python 2.7 code, type checkers should treat theint andlong types as equivalent. For parameters typed asText, arguments oftypestr as well asunicode should be acceptable.

Rejected Alternatives

During discussion of earlier drafts of this PEP, various objectionswere raised and alternatives were proposed. We discuss some of thesehere and explain why we reject them.

Several main objections were raised.

Which brackets for generic type parameters?

Most people are familiar with the use of angular brackets(e.g.List<int>) in languages like C++, Java, C# and Swift toexpress the parameterization of generic types. The problem with theseis that they are really hard to parse, especially for a simple-mindedparser like Python. In most languages the ambiguities are usuallydealt with by only allowing angular brackets in specific syntacticpositions, where general expressions aren’t allowed. (And also byusing very powerful parsing techniques that can backtrack over anarbitrary section of code.)

But in Python, we’d like type expressions to be (syntactically) thesame as other expressions, so that we can use e.g. variable assignmentto create type aliases. Consider this simple type expression:

List<int>

From the Python parser’s perspective, the expression begins with thesame four tokens (NAME, LESS, NAME, GREATER) as a chained comparison:

a<b>c# I.e., (a < b) and (b > c)

We can even make up an example that could be parsed both ways:

a<b>[c]

Assuming we had angular brackets in the language, this could beinterpreted as either of the following two:

(a<b>)[c]# I.e., (a<b>).__getitem__(c)a<b>([c])# I.e., (a < b) and (b > [c])

It would surely be possible to come up with a rule to disambiguatesuch cases, but to most users the rules would feel arbitrary andcomplex. It would also require us to dramatically change the CPythonparser (and every other parser for Python). It should be noted thatPython’s current parser is intentionally “dumb” – a simple grammar iseasier for users to reason about.

For all these reasons, square brackets (e.g.List[int]) are (andhave long been) the preferred syntax for generic type parameters.They can be implemented by defining the__getitem__() method onthe metaclass, and no new syntax is required at all. This optionworks in all recent versions of Python (starting with Python 2.2).Python is not alone in this syntactic choice – generic classes inScala also use square brackets.

What about existing uses of annotations?

One line of argument points out thatPEP 3107 explicitly supportsthe use of arbitrary expressions in function annotations. The newproposal is then considered incompatible with the specification of PEP3107.

Our response to this is that, first of all, the current proposal doesnot introduce any direct incompatibilities, so programs usingannotations in Python 3.4 will still work correctly and withoutprejudice in Python 3.5.

We do hope that type hints will eventually become the sole use forannotations, but this will require additional discussion and adeprecation period after the initial roll-out of the typing modulewith Python 3.5. The current PEP will have provisional status (seePEP 411) until Python 3.6 is released. The fastest conceivable schemewould introduce silent deprecation of non-type-hint annotations in3.6, full deprecation in 3.7, and declare type hints as the onlyallowed use of annotations in Python 3.8. This should give authors ofpackages that use annotations plenty of time to devise anotherapproach, even if type hints become an overnight success.

(UPDATE: As of fall 2017, the timeline for the end of provisionalstatus for this PEP and for thetyping.py module has changed, andso has the deprecation schedule for other uses of annotations. Forthe updated schedule seePEP 563.)

Another possible outcome would be that type hints will eventuallybecome the default meaning for annotations, but that there will alwaysremain an option to disable them. For this purpose the currentproposal defines a decorator@no_type_check which disables thedefault interpretation of annotations as type hints in a given classor function. It also defines a meta-decorator@no_type_check_decorator which can be used to decorate a decorator(!), causing annotations in any function or class decorated with thelatter to be ignored by the type checker.

There are also#type:ignore comments, and static checkers shouldsupport configuration options to disable type checking in selectedpackages.

Despite all these options, proposals have been circulated to allowtype hints and other forms of annotations to coexist for individualarguments. One proposal suggests that if an annotation for a givenargument is a dictionary literal, each key represents a different formof annotation, and the key'type' would be use for type hints.The problem with this idea and its variants is that the notationbecomes very “noisy” and hard to read. Also, in most cases whereexisting libraries use annotations, there would be little need tocombine them with type hints. So the simpler approach of selectivelydisabling type hints appears sufficient.

The problem of forward declarations

The current proposal is admittedly sub-optimal when type hints mustcontain forward references. Python requires all names to be definedby the time they are used. Apart from circular imports this is rarelya problem: “use” here means “look up at runtime”, and with most“forward” references there is no problem in ensuring that a name isdefined before the function using it is called.

The problem with type hints is that annotations (perPEP 3107, andsimilar to default values) are evaluated at the time a function isdefined, and thus any names used in an annotation must be alreadydefined when the function is being defined. A common scenario is aclass definition whose methods need to reference the class itself intheir annotations. (More general, it can also occur with mutuallyrecursive classes.) This is natural for container types, forexample:

classNode:"""Binary tree node."""def__init__(self,left:Node,right:Node):self.left=leftself.right=right

As written this will not work, because of the peculiarity in Pythonthat class names become defined once the entire body of the class hasbeen executed. Our solution, which isn’t particularly elegant, butgets the job done, is to allow using string literals in annotations.Most of the time you won’t have to use this though – mostuses oftype hints are expected to reference builtin types or types defined inother modules.

A counterproposal would change the semantics of type hints so theyaren’t evaluated at runtime at all (after all, type checking happensoff-line, so why would type hints need to be evaluated at runtime atall). This of course would run afoul of backwards compatibility,since the Python interpreter doesn’t actually know whether aparticular annotation is meant to be a type hint or something else.

A compromise is possible where a__future__ import could enableturningall annotations in a given module into string literals, asfollows:

from__future__importannotationsclassImSet:defadd(self,a:ImSet)->List[ImSet]:...assertImSet.add.__annotations__=={'a':'ImSet','return':'List[ImSet]'}

Such a__future__ import statement may be proposed in a separatePEP.

(UPDATE: That__future__ import statement and its consequencesare discussed inPEP 563.)

The double colon

A few creative souls have tried to invent solutions for this problem.For example, it was proposed to use a double colon (::) for typehints, solving two problems at once: disambiguating between type hintsand other annotations, and changing the semantics to preclude runtimeevaluation. There are several things wrong with this idea, however.

  • It’s ugly. The single colon in Python has many uses, and all ofthem look familiar because they resemble the use of the colon inEnglish text. This is a general rule of thumb by which Pythonabides for most forms of punctuation; the exceptions are typicallywell known from other programming languages. But this use of::is unheard of in English, and in other languages (e.g. C++) it isused as a scoping operator, which is a very different beast. Incontrast, the single colon for type hints reads naturally – and nowonder, since it was carefully designed for this purpose(the idealong predatesPEP 3107). It is also used in the samefashion in other languages from Pascal to Swift.
  • What would you do for return type annotations?
  • It’s actually a feature that type hints are evaluated at runtime.
    • Making type hints available at runtime allows runtime typecheckers to be built on top of type hints.
    • It catches mistakes even when the type checker is not run. Sinceit is a separate program, users may choose not to run it (or eveninstall it), but might still want to use type hints as a conciseform of documentation. Broken type hints are no use even fordocumentation.
  • Because it’s new syntax, using the double colon for type hints wouldlimit them to code that works with Python 3.5 only. By usingexisting syntax, the current proposal can easily work for olderversions of Python 3. (And in fact mypy supports Python 3.2 andnewer.)
  • If type hints become successful we may well decide to add new syntaxin the future to declare the type for variables, for examplevarage:int=42. If we were to use a double colon forargument type hints, for consistency we’d have to use the sameconvention for future syntax, perpetuating the ugliness.

Other forms of new syntax

A few other forms of alternative syntax have been proposed, e.g.theintroduction of awhere keyword, and Cobra-inspiredrequires clauses. But these all share a problem with the doublecolon: they won’t work for earlier versions of Python 3. The samewould apply to a new__future__ import.

Other backwards compatible conventions

The ideas put forward include:

  • A decorator, e.g.@typehints(name=str,returns=str). This couldwork, but it’s pretty verbose (an extra line, and the argument namesmust be repeated), and a far cry in elegance from thePEP 3107notation.
  • Stub files. We do want stub files, but they are primarily usefulfor adding type hints to existing code that doesn’t lend itself toadding type hints, e.g. 3rd party packages, code that needs tosupport both Python 2 and Python 3, and especially extensionmodules. For most situations, having the annotations in line withthe function definitions makes them much more useful.
  • Docstrings. There is an existing convention for docstrings, basedon the Sphinx notation (:typearg1:description). This ispretty verbose (an extra line per parameter), and not very elegant.We could also make up something new, but the annotation syntax ishard to beat (because it was designed for this very purpose).

It’s also been proposed to simply wait another release. But whatproblem would that solve? It would just be procrastination.

PEP Development Process

A live draft for this PEP lives onGitHub. There is also anissue tracker, where much of the technical discussion takesplace.

The draft on GitHub is updated regularly in small increments. Theofficial PEPS repo is (usually) only updated when a new draftis posted to python-dev.

Acknowledgements

This document could not be completed without valuable input,encouragement and advice from Jim Baker, Jeremy Siek, Michael MatsonVitousek, Andrey Vlasovskikh, Radomir Dopieralski, Peter Ludemann,and the BDFL-Delegate, Mark Shannon.

Influences include existing languages, libraries and frameworksmentioned inPEP 482. Many thanks to their creators, in alphabeticalorder: Stefan Behnel, William Edwards, Greg Ewing, Larry Hastings,Anders Hejlsberg, Alok Menghrajani, Travis E. Oliphant, Joe Pamer,Raoul-Gabriel Urma, and Julien Verlaguet.

Copyright

This document has been placed in the public domain.


Source:https://github.com/python/peps/blob/main/peps/pep-0484.rst

Last modified:2025-02-01 08:59:27 GMT


[8]ページ先頭

©2009-2025 Movatter.jp