Because Python’s 2.x series lacks a standard way of annotating a
function’s parameters and return values, a variety of tools
and libraries have appeared to fill this gap. Some
utilise the decorators introduced in PEP 318, while others parse a
function’s docstring, looking for annotations there.
This PEP aims to provide a single, standard way of specifying this
information, reducing the confusion caused by the wide variation in
mechanism and syntax that has existed until this point.
Before launching into a discussion of the precise ins and outs of
Python 3.0’s function annotations, let’s first talk broadly about
what annotations are and are not:
Function annotations, both for parameters and return values, are
completely optional.
Function annotations are nothing more than a way of associating
arbitrary Python expressions with various parts of a function at
compile-time.
By itself, Python does not attach any particular meaning or
significance to annotations. Left to its own, Python simply makes
these expressions available as described in Accessing Function
Annotations below.
The only way that annotations take on meaning is when they are
interpreted by third-party libraries. These annotation consumers
can do anything they want with a function’s annotations. For
example, one library might use string-based annotations to provide
improved 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 Python
functions and methods. This library could use annotations to
indicate the function’s expected input and return types, possibly
something like:
However, neither the strings in the first example nor the
type information in the second example have any meaning on their
own; meaning comes from third-party libraries alone.
Following from point 2, this PEP makes no attempt to introduce
any kind of standard semantics, even for the built-in types.
This work will be left to third-party libraries.
Annotations for parameters take the form of optional expressions that
follow the parameter name:
deffoo(a:expression,b:expression=5):...
In pseudo-grammar, parameters now look like identifier[:expression][=expression]. That is, annotations always precede a
parameter’s default value and both annotations and default values are
optional. Just like how equal signs are used to indicate a default
value, colons are used to mark annotations. All annotation
expressions 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 the
parameter, not the last parenthesis. Annotating all parameters of a
nested parameter is not required:
The examples thus far have omitted examples of how to annotate the
type 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, this
expression will be evaluated when the function definition is executed.
lambda’s syntax does not support annotations. The syntax of
lambda could be changed to support annotations, by requiring
parentheses around the parameter list. However it was decided
[12] not to make this change because:
Once compiled, a function’s annotations are available via the
function’s __annotations__ attribute. This attribute is
a mutable dictionary, mapping parameter names to an object
representing the evaluated annotation expression
There is a special key in the __annotations__ mapping,
"return". This key is present only if an annotation was supplied
for 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}
The return key was chosen because it cannot conflict with the name
of a parameter; any attempt to use return as a parameter name
would result in a SyntaxError.
__annotations__ is an empty, mutable dictionary if there are no
annotations on the function or if the functions was created from
a lambda expression.
In the course of discussing annotations, a number of use-cases have
been raised. Some of these are presented here, grouped by what kind
of information they convey. Also included are examples of existing
products and packages that could make use of annotations.
The BDFL rejected the author’s idea for a special syntax for adding
annotations to generators as being “too ugly” [2].
Though discussed early on ([5], [6]), including
special objects in the stdlib for annotating generator functions and
higher-order functions was ultimately rejected as being more
appropriate for third-party libraries; including them in the
standard library raised too many thorny issues.
Despite considerable discussion about a standard type
parameterisation syntax, it was decided that this should also be
left to third-party libraries. ([7],
[8], [9]).
Despite yet more discussion, it was decided not to standardize
a mechanism for annotation interoperability. Standardizing
interoperability conventions at this point would be premature.
We would rather let these conventions develop organically, based
on real-world usage and necessity, than try to force all users
into some contrived scheme. ([13], [14],
[15]).
This document has been placed in the public domain.
You are visitor 01402503 — GeezerVison does not store the pages you look up. Best viewed with Netscape Navigator 4.7 or Internet Explorer 6 at 800×600. No cookies required.