2013-10-08 19:23:55 -04:00
|
|
|
|
PEP: 457
|
2019-05-09 17:59:35 -04:00
|
|
|
|
Title: Notation For Positional-Only Parameters
|
2013-10-08 19:23:55 -04:00
|
|
|
|
Version: $Revision$
|
|
|
|
|
Last-Modified: $Date$
|
|
|
|
|
Author: Larry Hastings <larry@hastings.org>
|
2022-02-27 17:46:36 -05:00
|
|
|
|
Discussions-To: python-dev@python.org
|
2019-11-08 16:56:29 -05:00
|
|
|
|
Status: Final
|
2013-10-08 19:23:55 -04:00
|
|
|
|
Type: Informational
|
|
|
|
|
Content-Type: text/x-rst
|
|
|
|
|
Created: 08-Oct-2013
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
========
|
|
|
|
|
Overview
|
|
|
|
|
========
|
|
|
|
|
|
2019-05-09 17:59:35 -04:00
|
|
|
|
This PEP proposes a notation for positional-only parameters in Python.
|
2013-10-08 19:23:55 -04:00
|
|
|
|
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.
|
|
|
|
|
|
2019-05-09 17:59:35 -04:00
|
|
|
|
This PEP is an Informational PEP describing the notation for use when
|
|
|
|
|
describing APIs that use positional-only parameters (e.g. in Argument
|
2023-09-01 15:30:14 -04:00
|
|
|
|
Clinic, or in the string representation of ``inspect.Signature``
|
2022-01-21 06:03:51 -05:00
|
|
|
|
objects). A separate PEP, :pep:`570`, proposes elevation of this notation
|
2019-05-09 17:59:35 -04:00
|
|
|
|
to full Python syntax.
|
|
|
|
|
|
2013-10-08 19:23:55 -04:00
|
|
|
|
=========
|
|
|
|
|
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
|
2021-09-17 14:18:24 -04:00
|
|
|
|
expressible with Python syntax.
|
2013-10-08 19:23:55 -04:00
|
|
|
|
|
2019-05-09 17:59:35 -04:00
|
|
|
|
This PEP proposes a notation for such signatures that could form the
|
|
|
|
|
basis of a backwards-compatible syntax that should permit implementing
|
2022-01-21 06:03:51 -05:00
|
|
|
|
any builtin in pure Python code (see :pep:`570` for that proposal).
|
2013-10-08 19:23:55 -04:00
|
|
|
|
|
|
|
|
|
-----------------------------------------------------
|
|
|
|
|
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:
|
|
|
|
|
|
2016-05-03 04:18:02 -04:00
|
|
|
|
* ``range()``, which accepts an optional parameter
|
|
|
|
|
to the *left* of its required parameter. [#RANGE]_
|
2013-10-08 19:23:55 -04:00
|
|
|
|
|
2016-05-03 04:18:02 -04:00
|
|
|
|
* ``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]_
|
2013-10-08 19:23:55 -04:00
|
|
|
|
|
|
|
|
|
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:
|
|
|
|
|
|
2016-05-03 04:18:02 -04:00
|
|
|
|
* Documentation can clearly, unambiguously, and
|
|
|
|
|
consistently express exactly how the arguments
|
|
|
|
|
for a function will be interpreted.
|
2013-10-08 19:23:55 -04:00
|
|
|
|
|
2016-05-03 04:18:02 -04:00
|
|
|
|
* The syntax is reserved for future use, in case
|
|
|
|
|
the community decides someday to add positional-only
|
|
|
|
|
parameters to the language.
|
2013-10-08 19:23:55 -04:00
|
|
|
|
|
2016-05-03 04:18:02 -04:00
|
|
|
|
* Argument Clinic can use a variant of the syntax
|
|
|
|
|
as part of its input when defining
|
|
|
|
|
the arguments for built-in functions.
|
2013-10-08 19:23:55 -04:00
|
|
|
|
|
|
|
|
|
=================================================================
|
|
|
|
|
The Current State Of Documentation For Positional-Only Parameters
|
|
|
|
|
=================================================================
|
|
|
|
|
|
|
|
|
|
The documentation for positional-only parameters is incomplete
|
|
|
|
|
and inconsistent:
|
|
|
|
|
|
2019-05-14 07:16:24 -04:00
|
|
|
|
* Some functions denote optional *groups* of positional-only arguments
|
2013-10-08 19:23:55 -04:00
|
|
|
|
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.
|
|
|
|
|
|
2019-05-14 07:16:24 -04:00
|
|
|
|
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.
|
2013-10-08 19:23:55 -04:00
|
|
|
|
|
|
|
|
|
More semantics of positional-only parameters:
|
|
|
|
|
|
2016-05-03 04:18:02 -04:00
|
|
|
|
* 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``.)
|
2013-10-08 19:23:55 -04:00
|
|
|
|
|
2016-05-03 04:18:02 -04:00
|
|
|
|
* 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.
|
2013-10-08 19:23:55 -04:00
|
|
|
|
|
2016-05-03 04:18:02 -04:00
|
|
|
|
* This syntax has no effect on ``*args`` or ``**kwargs``.
|
2013-10-08 19:23:55 -04:00
|
|
|
|
|
|
|
|
|
======================
|
|
|
|
|
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.)
|
|
|
|
|
|
2019-05-14 07:16:24 -04:00
|
|
|
|
* 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.
|
2013-10-08 19:23:55 -04:00
|
|
|
|
|
|
|
|
|
==============================
|
|
|
|
|
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.
|
2013-10-08 20:56:36 -04:00
|
|
|
|
I propose that the value be called ``undefined``,
|
2013-10-08 19:23:55 -04:00
|
|
|
|
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:
|
|
|
|
|
|
2016-05-03 04:18:02 -04:00
|
|
|
|
1. positional-only parameters,
|
|
|
|
|
2. positional-or-keyword parameters, and
|
|
|
|
|
3. keyword-only parameters.
|
2013-10-08 19:23:55 -04:00
|
|
|
|
|
|
|
|
|
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
|
2023-10-11 08:05:51 -04:00
|
|
|
|
Alyssa Coghlan. (Conversation in person at PyCon US 2013.)
|
2013-10-08 19:23:55 -04:00
|
|
|
|
|
|
|
|
|
.. [#DICT]
|
|
|
|
|
http://docs.python.org/3/library/stdtypes.html#dict
|
2017-03-24 17:11:33 -04:00
|
|
|
|
|
2013-10-08 19:23:55 -04:00
|
|
|
|
.. [#RANGE]
|
|
|
|
|
http://docs.python.org/3/library/functions.html#func-range
|
2017-03-24 17:11:33 -04:00
|
|
|
|
|
2013-10-08 19:23:55 -04:00
|
|
|
|
.. [#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:
|
2017-06-11 15:02:39 -04:00
|
|
|
|
https://mail.python.org/pipermail/python-ideas/2012-March/014364.html
|
2013-10-08 19:23:55 -04:00
|
|
|
|
and
|
2017-06-11 15:02:39 -04:00
|
|
|
|
https://mail.python.org/pipermail/python-ideas/2012-March/014378.html
|
2013-10-08 19:23:55 -04:00
|
|
|
|
and
|
2017-06-11 15:02:39 -04:00
|
|
|
|
https://mail.python.org/pipermail/python-ideas/2012-March/014417.html
|
2013-10-08 19:23:55 -04:00
|
|
|
|
|
|
|
|
|
=========
|
|
|
|
|
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:
|