This PEP introduces a syntax for adding arbitrary metadata annotationsto Python functions[1].
Because Python’s 2.x series lacks a standard way of annotating afunction’s parameters and return values, a variety of toolsand libraries have appeared to fill this gap. Someutilise the decorators introduced inPEP 318, while others parse afunction’s docstring, looking for annotations there.
This PEP aims to provide a single, standard way of specifying thisinformation, reducing the confusion caused by the wide variation inmechanism and syntax that has existed until this point.
Before launching into a discussion of the precise ins and outs ofPython 3.0’s function annotations, let’s first talk broadly aboutwhat annotations are and are not:
By itself, Python does not attach any particular meaning orsignificance to annotations. Left to its own, Python simply makesthese expressions available as described inAccessing FunctionAnnotations below.
The only way that annotations take on meaning is when they areinterpreted by third-party libraries. These annotation consumerscan do anything they want with a function’s annotations. Forexample, one library might use string-based annotations to provideimproved help messages, like so:
defcompile(source:"something compilable",filename:"where the compilable thing comes from",mode:"is this a single statement or a suite?"):...
Another library might be used to provide typechecking for Pythonfunctions and methods. This library could use annotations toindicate the function’s expected input and return types, possiblysomething like:
defhaul(item:Haulable,*vargs:PackAnimal)->Distance:...
However, neither the strings in the first example nor thetype information in the second example have any meaning on theirown; meaning comes from third-party libraries alone.
Annotations for parameters take the form of optional expressions thatfollow the parameter name:
deffoo(a:expression,b:expression=5):...
In pseudo-grammar, parameters now look likeidentifier[:expression][=expression]. That is, annotations always precede aparameter’s default value and both annotations and default values areoptional. Just like how equal signs are used to indicate a defaultvalue, colons are used to mark annotations. All annotationexpressions are evaluated when the function definition is executed,just like default values.
Annotations for excess parameters (i.e.,*args and**kwargs)are indicated similarly:
deffoo(*args:expression,**kwargs:expression):...
Annotations for nested parameters always follow the name of theparameter, not the last parenthesis. Annotating all parameters of anested parameter is not required:
deffoo((x1,y1:expression),(x2:expression,y2:expression)=(None,None)):...
The examples thus far have omitted examples of how to annotate thetype of a function’s return value. This is done like so:
defsum()->expression:...
That is, the parameter list can now be followed by a literal->and a Python expression. Like the annotations for parameters, thisexpression will be evaluated when the function definition is executed.
The grammar for function definitions[11] is now:
decorator:'@'dotted_name['('[arglist]')']NEWLINEdecorators:decorator+funcdef:[decorators]'def'NAMEparameters['->'test]':'suiteparameters:'('[typedargslist]')'typedargslist:((tfpdef['='test]',')*('*'[tname](','tname['='test])*[',''**'tname]|'**'tname)|tfpdef['='test](','tfpdef['='test])*[','])tname:NAME[':'test]tfpdef:tname|'('tfplist')'tfplist:tfpdef(','tfpdef)*[',']
lambda’s syntax does not support annotations. The syntax oflambda could be changed to support annotations, by requiringparentheses around the parameter list. However it was decided[12] not to make this change because:
Once compiled, a function’s annotations are available via thefunction’s__annotations__ attribute. This attribute isa mutable dictionary, mapping parameter names to an objectrepresenting the evaluated annotation expression
There is a special key in the__annotations__ mapping,"return". This key is present only if an annotation was suppliedfor the function’s return value.
For example, the following annotation:
deffoo(a:'x',b:5+6,c:list)->max(2,9):...
would result in an__annotations__ mapping of
{'a':'x','b':11,'c':list,'return':9}
Thereturn key was chosen because it cannot conflict with the nameof a parameter; any attempt to usereturn as a parameter namewould result in aSyntaxError.
__annotations__ is an empty, mutable dictionary if there are noannotations on the function or if the functions was created fromalambda expression.
In the course of discussing annotations, a number of use-cases havebeen raised. Some of these are presented here, grouped by what kindof information they convey. Also included are examples of existingproducts and packages that could make use of annotations.
Thepydoc module should display the function annotations whendisplaying help for a function. Theinspect module should changeto support annotations.
Function Signature Objects should expose the function’s annotations.TheParameter object may change or other changes may be warranted.
A reference implementation has been checked into the py3k (formerly“p3yk”) branch as revision 53170[10].
This document has been placed in the public domain.
Source:https://github.com/python/peps/blob/main/peps/pep-3107.rst
Last modified:2025-02-01 08:59:27 GMT