PEP: 457 Title: Notation For Positional-Only Parameters Version:
$Revision$ Last-Modified: $Date$ Author: Larry Hastings
<larry@hastings.org> Discussions-To: Python-Dev <python-dev@python.org>
Status: Final Type: Informational Content-Type: text/x-rst Created:
08-Oct-2013

======== Overview ========

This PEP proposes a notation for positional-only parameters in Python.
Positional-only parameters are parameters without an externally-usable
name; when a function accepting positional-only parameters is called,
positional arguments are mapped to these parameters based solely on
their position.

This PEP is an Informational PEP describing the notation for use when
describing APIs that use positional-only parameters (e.g. in Argument
Clinic, or in the string representation of `inspect.Signature` objects).
A separate PEP, PEP 570, proposes elevation of this notation to full
Python syntax.

========= Rationale =========

Python has always supported positional-only parameters. Early versions
of Python lacked the concept of specifying parameters by name, so
naturally all parameters were positional-only. This changed around
Python 1.0, when all parameters suddenly became positional-or-keyword.
But, even in current versions of Python, many CPython "builtin"
functions still only accept positional-only arguments.

Functions implemented in modern Python can accept an arbitrary number of
positional-only arguments, via the variadic `*args` parameter. However,
there is no Python syntax to specify accepting a specific number of
positional-only parameters. Put another way, there are many builtin
functions whose signatures are simply not expressable with Python
syntax.

This PEP proposes a notation for such signatures that could form the
basis of a backwards-compatible syntax that should permit implementing
any builtin in pure Python code (see PEP 570 for that proposal).

  -------------------------------------------------------
  Positional-Only Parameter Semantics In Current Python
  -------------------------------------------------------

There are many, many examples of builtins that only accept
positional-only parameters. The resulting semantics are easily
experienced by the Python programmer--just try calling one, specifying
its arguments by name::

    >>> pow(x=5, y=3)
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
    TypeError: pow() takes no keyword arguments

In addition, there are some functions with particularly interesting
semantics:

-   `range()`, which accepts an optional parameter to the *left* of its
    required parameter. \[\#RANGE\]\_

-   `dict()`, whose mapping/iterator parameter is optional and
    semantically must be positional-only. Any externally visible name
    for this parameter would occlude that name going into the `**kwarg`
    keyword variadic parameter dict! \[\#DICT\]\_

Obviously one can simulate any of these in pure Python code by accepting
`(*args, **kwargs)` and parsing the arguments by hand. But this results
in a disconnect between the Python function's signature and what it
actually accepts, not to mention the work of implementing said argument
parsing.

========== Motivation ==========

This PEP does not propose we implement positional-only parameters in
Python. The goal of this PEP is simply to define the syntax, so that:

-   Documentation can clearly, unambiguously, and consistently express
    exactly how the arguments for a function will be interpreted.

-   The syntax is reserved for future use, in case the community decides
    someday to add positional-only parameters to the language.

-   Argument Clinic can use a variant of the syntax as part of its input
    when defining the arguments for built-in functions.

================================================================= The
Current State Of Documentation For Positional-Only Parameters
=================================================================

The documentation for positional-only parameters is incomplete and
inconsistent:

-   Some functions denote optional *groups* of positional-only arguments
    by enclosing them in nested square brackets. \[\#BORDER\]\_

-   Some functions denote optional groups of positional-only arguments
    by presenting multiple prototypes with varying numbers of arguments.
    \[\#SENDFILE\]\_

-   Some functions use *both* of the above approaches. \[\#RANGE\]\_
    \[\#ADDCH\]\_

One more important idea to consider: currently in the documentation
there's no way to tell whether a function takes positional-only
parameters. `open()` accepts keyword arguments, `ord()` does not, but
there is no way of telling just by reading the documentation that this
is true.

==================== Syntax And Semantics ====================

From the "ten-thousand foot view", and ignoring `*args` and `**kwargs`
for now, the grammar for a function definition currently looks like
this::

    def name(positional_or_keyword_parameters, *, keyword_only_parameters):

Building on that perspective, the new syntax for functions would look
like this::

    def name(positional_only_parameters, /, positional_or_keyword_parameters,
             *, keyword_only_parameters):

All parameters before the `/` are positional-only. If `/` is not
specified in a function signature, that function does not accept any
positional-only parameters.

Positional-only parameters can have a default value, and if they do they
are optional. Positional-only parameters that don't have a default value
are "required" positional-only parameters.

More semantics of positional-only parameters:

-   Although positional-only parameter technically have names, these
    names are internal-only; positional-only parameters are *never*
    externally addressable by name. (Similarly to `*args` and
    `**kwargs`.)

-   If there are arguments after the `/`, then you must specify a comma
    after the `/`, just as there is a comma after the `*` denoting the
    shift to keyword-only parameters.

-   This syntax has no effect on `*args` or `**kwargs`.

====================== Additional Limitations ======================

Argument Clinic uses a form of this syntax for specifying builtins. It
imposes further limitations that are theoretically unnecessary but make
the implementation easier. Specifically:

-   A function that has positional-only parameters currently cannot have
    any other kind of parameter. (This will probably be relaxed slightly
    in the near future.)

-   Argument Clinic supports an additional syntax called "optional
    groups". An "optional group" is a sequential set of positional-only
    parameters that must be specified or not-specified as a group. If,
    for example, you define a function in Argument Clinic that takes
    four parameters, and all of them are positional-only and in one
    optional group, then when calling the function you must specify
    either zero arguments or four arguments. This is necessary to cover
    more of Python's legacy library, but is outside the scope of this
    PEP, and is not recommended for actual inclusion in the Python
    language.

============================== Notes For A Future Implementor
==============================

If we decide to implement positional-only parameters in a future version
of Python, we'd have to do some additional work to preserve their
semantics. The problem: how do we inform a parameter that no value was
passed in for it when the function was called?

The obvious solution: add a new singleton constant to Python that is
passed in when a parameter is not mapped to an argument. I propose that
the value be called `undefined`, and be a singleton of a special class
called `Undefined`. If a positional-only parameter did not receive an
argument when called, its value would be set to `undefined`.

But this raises a further problem. How do can we tell the difference
between "this positional-only parameter did not receive an argument" and
"the caller passed in `undefined` for this parameter"?

It'd be nice to make it illegal to pass `undefined` in as an argument to
a function--to, say, raise an exception. But that would slow Python
down, and the "consenting adults" rule appears applicable here. So
making it illegal should probably be strongly discouraged but not
outright prevented.

However, it should be allowed (and encouraged) for user functions to
specify `undefined` as a default value for parameters.

==================== Unresolved Questions ====================

There are three types of parameters in Python:

1.  positional-only parameters,
2.  positional-or-keyword parameters, and
3.  keyword-only parameters.

Python allows functions to have both 2 and 3. And some builtins
(e.g. range) have both 1 and 3. Does it make sense to have functions
that have both 1 and 2? Or all of the above?

====== Thanks ======

Credit for the use of '/' as the separator between positional-only and
positional-or-keyword parameters goes to Guido van Rossum, in a proposal
from 2012. \[\#GUIDO\]\_

Credit for making left option groups higher precedence goes to Nick
Coghlan. (Conversation in person at PyCon US 2013.)

.. \[\#DICT\] http://docs.python.org/3/library/stdtypes.html\#dict

.. \[\#RANGE\]
http://docs.python.org/3/library/functions.html\#func-range

.. \[\#BORDER\]
http://docs.python.org/3/library/curses.html\#curses.window.border

.. \[\#SENDFILE\] http://docs.python.org/3/library/os.html\#os.sendfile

.. \[\#ADDCH\]
http://docs.python.org/3/library/curses.html\#curses.window.addch

.. \[\#GUIDO\] Guido van Rossum, posting to python-ideas, March 2012:
https://mail.python.org/pipermail/python-ideas/2012-March/014364.html
and
https://mail.python.org/pipermail/python-ideas/2012-March/014378.html
and
https://mail.python.org/pipermail/python-ideas/2012-March/014417.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:
