python-peps/pep-3107.txt

294 lines
9.4 KiB
Plaintext
Raw Normal View History

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
2006-12-23 01:10:01 -05:00
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
2006-12-23 01:10:01 -05:00
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
2006-12-23 01:10:01 -05:00
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'
2006-12-23 01:10:01 -05:00
and 'c' should both be a ``Number``, while parameter 'b' should
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.
2006-12-23 01:10:01 -05:00
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: