added PEP 3107, Function Annotations, by Winter & Lownds
This commit is contained in:
parent
9eb151a284
commit
6006113241
|
@ -105,6 +105,7 @@ Index by Category
|
|||
S 3104 Access to Names in Outer Scopes Yee
|
||||
S 3105 Make print a function Brandl
|
||||
S 3106 Revamping dict.keys(), .values() and .items() GvR
|
||||
S 3107 Function Annotations Winter, Lownds
|
||||
|
||||
Finished PEPs (done, implemented in Subversion)
|
||||
|
||||
|
@ -443,6 +444,7 @@ Numerical Index
|
|||
S 3104 Access to Names in Outer Scopes Yee
|
||||
S 3105 Make print a function Brandl
|
||||
S 3106 Revamping dict.keys(), .values() and .items() GvR
|
||||
S 3107 Function Annotations Winter, Lownds
|
||||
|
||||
Key
|
||||
|
||||
|
@ -513,6 +515,7 @@ Owners
|
|||
Lielens, Gregory gregory.lielens@fft.be
|
||||
Lindqvist, Björn bjourne@gmail.com
|
||||
von Loewis, Martin loewis@informatik.hu-berlin.de
|
||||
Lownds, Tony tony@pagedna.com
|
||||
Martelli, Alex aleax@aleax.it
|
||||
McClelland, Andrew eternalsquire@comcast.net
|
||||
McMillan, Gordon gmcm@hypernet.com
|
||||
|
@ -549,6 +552,7 @@ Owners
|
|||
Way, Terence terry@wayforward.net
|
||||
Wells, Cliff LogiplexSoftware@earthlink.net
|
||||
Wilson, Greg gvwilson@ddj.com
|
||||
Winter, Collin collinw@gmail.com
|
||||
Wouters, Thomas thomas@xs4all.net
|
||||
Yee, Ka-Ping ping@zesty.ca
|
||||
Zadka, Moshe moshez@zadka.site.co.il
|
||||
|
|
|
@ -0,0 +1,294 @@
|
|||
PEP: 3107
|
||||
Title: Function Annotations
|
||||
Version: $Revision$
|
||||
Last-Modified: $Date$
|
||||
Author: Collin Winter <collinw@gmail.com>,
|
||||
Tony Lownds <tony@pagedna.com>
|
||||
Status: Draft
|
||||
Type: Standards Track
|
||||
Requires: 362
|
||||
Content-Type: text/x-rst
|
||||
Created: 2-Dec-2006
|
||||
Python-Version: 3.0
|
||||
Post-History:
|
||||
|
||||
|
||||
Abstract
|
||||
========
|
||||
|
||||
This PEP introduces a syntax for adding arbitrary metadata annotations
|
||||
to Python functions [#functerm]_.
|
||||
|
||||
|
||||
Rationale
|
||||
=========
|
||||
|
||||
Because Python's 2.x series lacks a standard way of annotating a
|
||||
function's parameters and return values (e.g., with information about
|
||||
a what type a function's return value should be), a variety of tools
|
||||
and libraries have appeared to fill this gap [#tailexamp]_. 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.
|
||||
|
||||
|
||||
Fundamentals of Function Annotations
|
||||
====================================
|
||||
|
||||
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:
|
||||
|
||||
1. Function annotations, both for parameters and return values, are
|
||||
completely optional.
|
||||
|
||||
2. 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 the values in the
|
||||
``__signature__.annotations`` mapping 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::
|
||||
|
||||
def compile(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 ::
|
||||
|
||||
def haul(item: Haulable, *vargs: PackAnimal) -> Distance:
|
||||
...
|
||||
|
||||
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.
|
||||
|
||||
3. 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.
|
||||
|
||||
There is no worry that these libraries will assign semantics at
|
||||
random, or that a variety of libraries will appear, each with
|
||||
varying semantics and interpretations of what, say, a tuple of
|
||||
strings means. The difficulty inherent in writing annotation
|
||||
interpreting libraries will keep their number low and their
|
||||
authorship in the hands of people who, frankly, know what they're
|
||||
doing.
|
||||
|
||||
|
||||
Syntax
|
||||
======
|
||||
|
||||
Parameters
|
||||
----------
|
||||
|
||||
Annotations for parameters take the form of optional expressions that
|
||||
follow the parameter name. This example indicates that parameters 'a'
|
||||
and 'c' should both be a ``Number``, while parameter 'b' should both
|
||||
be a ``Mapping``::
|
||||
|
||||
def foo(a: Number, b: Mapping, c: Number = 5):
|
||||
...
|
||||
|
||||
(Number, Mapping and Sequence are used thoughout this PEP and
|
||||
represent the standard `numeric`_, `mapping`_ and `sequence
|
||||
protocols`_.)
|
||||
|
||||
In pseudo-grammar, parameters now look like ``identifier [:
|
||||
expression] [= expression]``. That is, type annotations always
|
||||
precede a parameter's default value and both type 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.
|
||||
|
||||
Annotations for excess parameters (i.e., ``*args`` and ``**kwargs``)
|
||||
are indicated similarly. In the following function definition,
|
||||
``*args`` is flagged as a list of ``Number``, and ``**kwargs`` is
|
||||
marked as a dict whose keys are strings and whose values are of type
|
||||
``Sequence``. ::
|
||||
|
||||
def foo(*args: Number, **kwargs: Sequence):
|
||||
...
|
||||
|
||||
Note that, depending on what annotation-interpreting library you're
|
||||
using, the following might also be a valid spelling of the above::
|
||||
|
||||
def foo(*args: [Number], **kwargs: {str: Sequence}):
|
||||
...
|
||||
|
||||
Only the first, however, has the BDFL's blessing [#blessedexcess]_ as
|
||||
the One Obvious Way.
|
||||
|
||||
|
||||
Return Values
|
||||
-------------
|
||||
|
||||
The examples thus far have omitted examples of how to annotate the
|
||||
type of a function's return value. This is done like so::
|
||||
|
||||
def sum(*args: Number) -> Number:
|
||||
...
|
||||
|
||||
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.
|
||||
|
||||
The grammar for function definitions [#grammar]_ is now something like
|
||||
::
|
||||
|
||||
funcdef ::= [decorators] "def" funcname "("
|
||||
[parameter_list] ")" ["->" expression] ":" suite
|
||||
decorators ::= decorator+
|
||||
decorator ::= "@" dotted_name ["(" [argument_list
|
||||
[","]] ")"] NEWLINE
|
||||
dotted_name ::= identifier ("." identifier)*
|
||||
parameter_list ::= (defparameter ",")*
|
||||
( "*" identifier [":" expression]
|
||||
[, "**" identifier [":" expression] ]
|
||||
| "**" identifier [":" expression]
|
||||
| defparameter [","] )
|
||||
defparameter ::= parameter [":" expression] ["=" expression]
|
||||
sublist ::= parameter ("," parameter)* [","]
|
||||
parameter ::= identifier | "(" sublist ")"
|
||||
funcname ::= identifier
|
||||
|
||||
|
||||
Accessing Function Annotations
|
||||
==============================
|
||||
|
||||
Once compiled, a function's annotations are available via the
|
||||
function's ``__signature__`` attribute, introduced by PEP 362.
|
||||
Signature objects include an attribute just for annotations,
|
||||
appropriately called ``annotations``. This attribute is a 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::
|
||||
|
||||
def foo(a: Number, b: 5 + 6, c: list) -> String:
|
||||
...
|
||||
|
||||
would result in a ``__signature__.annotations`` mapping of ::
|
||||
|
||||
{'a': Number,
|
||||
'b': 11,
|
||||
'c': list,
|
||||
'return': String}
|
||||
|
||||
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``.
|
||||
|
||||
|
||||
Implementation
|
||||
==============
|
||||
|
||||
A sample implementation has been provided [#implementation]_ by Tony
|
||||
Lownds.
|
||||
|
||||
|
||||
Rejected Proposals
|
||||
==================
|
||||
|
||||
+ The BDFL rejected the author's idea for a special syntax for adding
|
||||
annotations to generators as being "too ugly" [#rejectgensyn]_.
|
||||
|
||||
+ Though discussed early on ([#threadgen]_, [#threadhof]_), 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. ([#threadimmlist]_, [#threadmixing]_,
|
||||
[#emphasistpls]_)
|
||||
|
||||
|
||||
References and Footnotes
|
||||
========================
|
||||
|
||||
.. [#functerm] Unless specifically stated, "function" is generally
|
||||
used as a synonym for "callable" throughout this document.
|
||||
|
||||
.. [#tailexamp] The author's typecheck_ library makes use of
|
||||
decorators, while `Maxime Bourget's own typechecker`_ utilises
|
||||
parsed docstrings.
|
||||
|
||||
.. [#blessedexcess]
|
||||
http://mail.python.org/pipermail/python-3000/2006-May/002173.html
|
||||
|
||||
.. [#rejectgensyn]
|
||||
http://mail.python.org/pipermail/python-3000/2006-May/002103.html
|
||||
|
||||
.. _typecheck:
|
||||
http://oakwinter.com/code/typecheck/
|
||||
|
||||
.. _Maxime Bourget's own typechecker:
|
||||
http://maxrepo.info/taxonomy/term/3,6/all
|
||||
|
||||
.. [#threadgen]
|
||||
http://mail.python.org/pipermail/python-3000/2006-May/002091.html
|
||||
|
||||
.. [#threadhof]
|
||||
http://mail.python.org/pipermail/python-3000/2006-May/001972.html
|
||||
|
||||
.. [#threadimmlist]
|
||||
http://mail.python.org/pipermail/python-3000/2006-May/002105.html
|
||||
|
||||
.. [#threadmixing]
|
||||
http://mail.python.org/pipermail/python-3000/2006-May/002209.html
|
||||
|
||||
.. [#emphasistpls]
|
||||
http://mail.python.org/pipermail/python-3000/2006-June/002438.html
|
||||
|
||||
.. [#implementation]
|
||||
http://python.org/sf/1607548
|
||||
|
||||
.. _numeric:
|
||||
http://docs.python.org/lib/typesnumeric.html
|
||||
|
||||
.. _mapping:
|
||||
http://docs.python.org/lib/typesmapping.html
|
||||
|
||||
.. _sequence protocols:
|
||||
http://docs.python.org/lib/typesseq.html
|
||||
|
||||
.. [#grammar]
|
||||
http://www.python.org/doc/current/ref/function.html
|
||||
|
||||
|
||||
Copyright
|
||||
=========
|
||||
|
||||
This document has been placed in the public domain.
|
||||
|
||||
|
||||
..
|
||||
Local Variables:
|
||||
mode: indented-text
|
||||
indent-tabs-mode: nil
|
||||
sentence-end-double-space: t
|
||||
fill-column: 70
|
||||
coding: utf-8
|
||||
End:
|
Loading…
Reference in New Issue