2018-02-01 14:02:37 -05:00
|
|
|
|
PEP: 570
|
|
|
|
|
Title: Python Positional-Only Parameters
|
|
|
|
|
Version: $Revision$
|
|
|
|
|
Last-Modified: $Date$
|
|
|
|
|
Author: Larry Hastings <larry@hastings.org>,
|
2019-03-11 19:16:05 -04:00
|
|
|
|
Pablo Galindo <pablogsal@gmail.com>,
|
|
|
|
|
Mario Corchero <mariocj89@gmail.com>
|
2018-02-01 14:02:37 -05:00
|
|
|
|
Discussions-To: Python-Dev <python-dev@python.org>
|
|
|
|
|
Status: Draft
|
|
|
|
|
Type: Standards Track
|
|
|
|
|
Content-Type: text/x-rst
|
|
|
|
|
Created: 20-Jan-2018
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
========
|
|
|
|
|
Overview
|
|
|
|
|
========
|
|
|
|
|
|
|
|
|
|
This PEP proposes a syntax 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.
|
|
|
|
|
|
|
|
|
|
=========
|
|
|
|
|
Rationale
|
|
|
|
|
=========
|
|
|
|
|
|
|
|
|
|
Python has always supported positional-only parameters.
|
|
|
|
|
Early versions of Python lacked the concept of specifying
|
2019-03-27 17:03:51 -04:00
|
|
|
|
parameters by name, so naturally, all parameters were
|
|
|
|
|
positional-only. This changed around Python 1.0 when
|
2018-02-01 14:02:37 -05:00
|
|
|
|
all parameters suddenly became positional-or-keyword.
|
2019-03-27 17:03:51 -04:00
|
|
|
|
This change allowed users to provide arguments to a function
|
|
|
|
|
either positionally or referencing the keyword used in the
|
|
|
|
|
function's definition. However, this is not always desirable,
|
|
|
|
|
and in fact even in current versions of Python many CPython
|
|
|
|
|
"builtin" functions still only accept positional-only
|
|
|
|
|
arguments.
|
2018-02-01 14:02:37 -05:00
|
|
|
|
|
2019-03-05 16:59:06 -05:00
|
|
|
|
Users might want to restrict their API to not allow for parameters
|
2019-03-11 19:16:05 -04:00
|
|
|
|
to be referenced via keywords, as that exposes the name of the
|
2019-03-05 16:59:06 -05:00
|
|
|
|
parameter as part of the API. If a user of said API starts using the
|
2019-03-11 19:16:05 -04:00
|
|
|
|
argument by keyword when calling it and then the parameter
|
|
|
|
|
gets renamed, it will be a breaking change. By using positional-only
|
2019-03-27 17:03:51 -04:00
|
|
|
|
parameters the developer can later change the name of any arguments or
|
2019-03-05 16:59:06 -05:00
|
|
|
|
transform them to ``*args`` without breaking the API.
|
|
|
|
|
|
2019-03-11 19:16:05 -04:00
|
|
|
|
Even if making arguments positional-only in a function can be achieved
|
|
|
|
|
by using ``*args`` parameters and extracting them one by one,
|
2018-02-01 14:02:37 -05:00
|
|
|
|
the solution is far from ideal and not as expressive as the one
|
2019-03-11 19:16:05 -04:00
|
|
|
|
proposed in this PEP, which targets providing syntax to specify
|
2019-03-05 16:59:06 -05:00
|
|
|
|
accepting a specific number of positional-only parameters. Also,
|
|
|
|
|
it makes the signature of the function ambiguous as users won't
|
2019-03-11 19:16:05 -04:00
|
|
|
|
know how many parameters the function takes by looking at ``help()``
|
2019-03-05 16:59:06 -05:00
|
|
|
|
or auto-generated documentation.
|
|
|
|
|
|
2018-02-01 14:02:37 -05:00
|
|
|
|
Additionally, this will bridge the gap we currently find between
|
2019-03-11 19:16:05 -04:00
|
|
|
|
builtin functions that can specify positional-only
|
2018-02-01 14:02:37 -05:00
|
|
|
|
parameters and pure Python implementations that lack the
|
2019-03-05 16:59:06 -05:00
|
|
|
|
syntax for it. The '/' syntax is already exposed in the
|
2019-03-11 19:16:05 -04:00
|
|
|
|
documentation of some builtins and interfaces generated by
|
2019-03-27 17:03:51 -04:00
|
|
|
|
the argument clinic.
|
|
|
|
|
|
|
|
|
|
Making positional-only arguments a possibility in Python will make the
|
|
|
|
|
language more consistent and since it would be a normal feature of Python
|
|
|
|
|
rather than a feature exclusive to extension modules, it should reduce
|
|
|
|
|
surprise and confusion by users encountering functions with positional-only
|
|
|
|
|
arguments. Notably, major third-party packages are already using the "/"
|
|
|
|
|
notation in their interfaces [#numpy-ufuncs]_ [#scipy-gammaln]_.
|
|
|
|
|
|
|
|
|
|
Positional-only arguments may be useful in several situations. One of the more
|
|
|
|
|
extreme situations is in a function that can take any keyword parameter but
|
|
|
|
|
also can take a positional one. Well-known examples for this situation are
|
|
|
|
|
``Formatter.format`` and ``dict.update``. For instance, ``dict.update``
|
|
|
|
|
accepts a dictionary (positionally) and/or any set of keyword parameters to use
|
|
|
|
|
as key/value pairs. In this case, if the dictionary parameter were not
|
|
|
|
|
positional-only, the user could not use the name that the interface uses for
|
|
|
|
|
said parameter or, conversely, the function could not distinguish easily if
|
|
|
|
|
the parameter received is the dictionary or one key/value pair.
|
|
|
|
|
|
|
|
|
|
Another important scenario is when argument names do not have semantic meaning.
|
|
|
|
|
For example, let's say we want to create a function that converts from one type
|
|
|
|
|
to another::
|
2019-03-11 11:13:46 -04:00
|
|
|
|
|
|
|
|
|
def as_my_type(x):
|
|
|
|
|
...
|
|
|
|
|
|
2019-03-11 19:16:05 -04:00
|
|
|
|
The name of the parameter provides no value whatsoever, and forces
|
|
|
|
|
the developer to maintain its name forever, as users might pass ``x`` as a
|
|
|
|
|
keyword.
|
2019-03-11 11:13:46 -04:00
|
|
|
|
|
2019-03-27 17:03:51 -04:00
|
|
|
|
Another good example is an API that wants make it clear that one of its
|
|
|
|
|
parameters is the "main" argument through positional-only arguments.
|
|
|
|
|
For example, see::
|
|
|
|
|
|
|
|
|
|
def add_to_queue(item: QueueItem):
|
|
|
|
|
...
|
|
|
|
|
|
|
|
|
|
Again we get no value from using keyword arguments here, and it can limit
|
|
|
|
|
future evolution of the API. Say at a later time we want this function
|
|
|
|
|
to be able to take multiple items while preserving backwards compatibility::
|
|
|
|
|
|
|
|
|
|
def add_to_queue(items: Union[QueueItem, List[QueueItem]]):
|
|
|
|
|
...
|
|
|
|
|
|
|
|
|
|
or to take them by using argument lists::
|
|
|
|
|
|
|
|
|
|
def add_to_queue(*items: QueueItem):
|
|
|
|
|
...
|
|
|
|
|
|
|
|
|
|
we will be forced to always keep the original argument or we would
|
|
|
|
|
potentially break users. By being able to define positional-only arguments,
|
|
|
|
|
we can change the name of the parameters at will or even change them to
|
|
|
|
|
``*args`` as in the previous example. There are multiple interfaces in the
|
|
|
|
|
standard library that fall into this category, for example the "main"
|
|
|
|
|
argument of ``collections.defaultdict`` (called *default_factory* in its
|
|
|
|
|
documentation) can only be passed positionally. One special case of this
|
|
|
|
|
situation is the *self* parameter for class methods: it is undersired that
|
|
|
|
|
a user can bind by keyword to the name "self" when calling the method from
|
|
|
|
|
the class::
|
|
|
|
|
|
|
|
|
|
io.FileIO.write(self=f, b=b"data")
|
|
|
|
|
|
|
|
|
|
Indeed, interfaces from the standard library implemented in C usually take
|
|
|
|
|
"self" as a positional-only argument::
|
|
|
|
|
|
|
|
|
|
>>> help(io.FileIO.write)
|
|
|
|
|
Help on method_descriptor:
|
2019-03-11 11:13:46 -04:00
|
|
|
|
|
2019-03-27 17:03:51 -04:00
|
|
|
|
write(self, b, /)
|
|
|
|
|
Write buffer b to file, return number of bytes written.
|
2019-03-11 11:13:46 -04:00
|
|
|
|
|
2019-03-27 17:03:51 -04:00
|
|
|
|
Another essential aspect to consider is PEP 399 [#PEP399]_, that mandates
|
|
|
|
|
that pure Python versions of modules in the standard library *must* have the
|
|
|
|
|
same interface and semantics that the accelerator modules implemented in C
|
|
|
|
|
(). For example, if ``collections.defaultdict`` were to have a pure Python
|
|
|
|
|
implementation it would need to make use of positional-only parameters to
|
|
|
|
|
match the interface of its C counterpart. A more detailed discussion about
|
|
|
|
|
this topic can be found in the Motivation_ section.
|
2019-03-11 11:13:46 -04:00
|
|
|
|
|
2019-03-11 19:16:05 -04:00
|
|
|
|
---------------------------------------------------
|
|
|
|
|
Positional-Only Parameter Semantics In Python Today
|
|
|
|
|
---------------------------------------------------
|
2018-02-01 14:02:37 -05:00
|
|
|
|
|
2019-03-27 17:03:51 -04:00
|
|
|
|
There are many, many examples of functions that only accept positional-only
|
|
|
|
|
parameters in the standard library. The resulting semantics are easily
|
|
|
|
|
experienced by the Python programmer -- just try calling one, specifying its
|
2018-02-01 14:02:37 -05:00
|
|
|
|
arguments by name::
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
>>> help(pow)
|
|
|
|
|
...
|
|
|
|
|
pow(x, y, z=None, /)
|
|
|
|
|
...
|
|
|
|
|
>>> pow(x=5, y=3)
|
|
|
|
|
Traceback (most recent call last):
|
|
|
|
|
File "<stdin>", line 1, in <module>
|
|
|
|
|
TypeError: pow() takes no keyword arguments
|
|
|
|
|
|
2019-03-11 19:16:05 -04:00
|
|
|
|
``pow()`` clearly expresses that its arguments are only positional
|
2019-03-27 17:03:51 -04:00
|
|
|
|
via the ``/`` marker, but this at the moment is only a documentation convention,
|
2018-02-01 14:02:37 -05:00
|
|
|
|
Python developers cannot write such syntax.
|
|
|
|
|
|
2019-03-27 17:03:51 -04:00
|
|
|
|
Besides, there are some functions with particularly
|
2018-02-01 14:02:37 -05:00
|
|
|
|
interesting semantics:
|
|
|
|
|
|
|
|
|
|
* ``range()``, which accepts an optional parameter
|
|
|
|
|
to the *left* of its required parameter. [#RANGE]_
|
|
|
|
|
|
2019-03-27 17:03:51 -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
|
2018-02-01 14:02:37 -05:00
|
|
|
|
parameter dict! [#DICT]_
|
|
|
|
|
|
2019-03-27 17:03:51 -04:00
|
|
|
|
One can simulate any of these in pure Python code
|
2018-02-01 14:02:37 -05:00
|
|
|
|
by accepting ``(*args, **kwargs)`` and parsing the arguments
|
2019-03-27 17:03:51 -04:00
|
|
|
|
by hand. However, this results in a disconnect between the
|
|
|
|
|
Python function signature and what the function accepts,
|
2019-03-05 16:59:06 -05:00
|
|
|
|
not to mention the work of implementing said argument parsing
|
2019-03-11 19:16:05 -04:00
|
|
|
|
and the lack of clarity in the resulting signature.
|
2018-02-01 14:02:37 -05:00
|
|
|
|
|
2019-03-27 17:03:51 -04:00
|
|
|
|
As mentioned before, this syntax is already being used outside the
|
|
|
|
|
CPython code base for similar use cases [#numpy-ufuncs]_ [#scipy-gammaln]_,
|
|
|
|
|
remarking that these scenarios are not exclusive to CPython and the
|
|
|
|
|
standard library.
|
|
|
|
|
|
|
|
|
|
Currently users are surprised when first encountering this notation, but this
|
|
|
|
|
is to be expected given that it has only recently been adequately documented
|
|
|
|
|
[#document-positional-only], and it is not possible to use it in Python code.
|
|
|
|
|
For these reasons, this notation is currently an oddity that appears only in
|
|
|
|
|
CPython's APIs developed in C. Documenting the notation and making it possible
|
|
|
|
|
to be used in Python code will certainly eliminate this problem.
|
|
|
|
|
|
2018-02-01 14:02:37 -05:00
|
|
|
|
==========
|
|
|
|
|
Motivation
|
|
|
|
|
==========
|
|
|
|
|
|
2019-03-27 17:03:51 -04:00
|
|
|
|
.. _Motivation:
|
|
|
|
|
|
2018-02-01 14:02:37 -05:00
|
|
|
|
The new syntax will allow developers to further control how their
|
2019-03-27 17:03:51 -04:00
|
|
|
|
API can be consumed. It will allow restricting certain arguments
|
|
|
|
|
to be positional-only, so they cannot be passed with a keyword.
|
2018-02-01 14:02:37 -05:00
|
|
|
|
|
2019-03-11 19:16:05 -04:00
|
|
|
|
A similar PEP with a broader scope (PEP 457) was proposed earlier
|
|
|
|
|
to define the syntax. This PEP builds partially on top of that,
|
|
|
|
|
to define and provide an implementation for the ``/`` syntax in
|
2018-02-01 14:02:37 -05:00
|
|
|
|
function signatures.
|
|
|
|
|
|
2019-03-27 17:03:51 -04:00
|
|
|
|
In addition to the API benefits outlined earlier in this document,
|
|
|
|
|
positional-only arguments are also faster, as demonstrated in this thread
|
|
|
|
|
about converting keyword arguments to positional:
|
|
|
|
|
[#thread-keyword-to-positional]_. In fact, because of these benefits there has
|
|
|
|
|
even been a recent trend towards moving builtins away from keyword arguments:
|
|
|
|
|
recently, backwards-incompatible changes were made to disallow keyword
|
|
|
|
|
arguments to ``bool``, ``float``, ``list``, ``int``, ``tuple``.
|
|
|
|
|
|
|
|
|
|
Providing a way to specify positional-only arguments in Python will make it
|
|
|
|
|
easier to maintain pure Python implementations of C modules and will allow
|
|
|
|
|
users to take advantage of these benefits even in code written only in Python.
|
|
|
|
|
It will also encourage users to start with positional-only arguments when they
|
|
|
|
|
believe that passing a keyword argument provides no clarity; unlike making a
|
|
|
|
|
keyword argument positional-only, allowing a positional argument to be passed
|
|
|
|
|
positionally is not a breaking change.
|
2019-03-11 11:13:46 -04:00
|
|
|
|
|
2019-03-11 19:16:05 -04:00
|
|
|
|
This is a well discussed, recurring topic on the Python mailing lists:
|
2019-03-11 11:13:46 -04:00
|
|
|
|
|
|
|
|
|
* September 2018: `Anders Hovmöller: [Python-ideas] Positional-only
|
|
|
|
|
parameters
|
|
|
|
|
<https://mail.python.org/pipermail/python-ideas/2018-September/053233.html>`_
|
|
|
|
|
* February 2017: `Victor Stinner: [Python-ideas] Positional-only
|
|
|
|
|
parameters
|
|
|
|
|
<https://mail.python.org/pipermail/python-ideas/2017-February/044879.html>`_,
|
|
|
|
|
`discussion continued in March
|
|
|
|
|
<https://mail.python.org/pipermail/python-ideas/2017-March/044956.html>`_
|
|
|
|
|
* February 2017: [#python-ideas-decorator-based]_
|
|
|
|
|
* March 2012: [#GUIDO]_
|
|
|
|
|
* May 2007: `George Sakkis: [Python-ideas] Positional only arguments
|
|
|
|
|
<https://mail.python.org/pipermail/python-ideas/2007-May/000704.html>`_
|
|
|
|
|
* May 2006: `Benji York: [Python-Dev] Positional-only Arguments
|
|
|
|
|
<https://mail.python.org/pipermail/python-dev/2006-May/064790.html>`_
|
|
|
|
|
|
2019-03-27 17:03:51 -04:00
|
|
|
|
Positional-only parameters have also the (minor) advantage of enforcing
|
|
|
|
|
some logical order when calling interfaces that make use of them. For
|
|
|
|
|
example, the ``range`` function takes all its parameters positionally and
|
|
|
|
|
this disallows forms like::
|
|
|
|
|
|
|
|
|
|
range(stop=5, start=0, step=2)
|
|
|
|
|
range(stop=5, step=2, start=0)
|
|
|
|
|
range(step=2, start=0, stop=5)
|
|
|
|
|
range(step=2, stop=5, start=0)
|
|
|
|
|
|
|
|
|
|
at the price of disallowing the use of keyword arguments for the (unique)
|
|
|
|
|
intended order::
|
|
|
|
|
|
|
|
|
|
range(start=0, stop=5, step=2)
|
|
|
|
|
|
|
|
|
|
Another critical aspect that motivates positional-only arguments is
|
|
|
|
|
PEP 399 [#PEP399]_: Pure Python/C Accelerator Module Compatibility
|
|
|
|
|
Requirements. This PEP states that::
|
|
|
|
|
|
|
|
|
|
This PEP requires that in these instances that the C code must pass
|
|
|
|
|
the test suite used for the pure Python code to act as much as
|
|
|
|
|
a drop-in replacement as reasonably possible
|
|
|
|
|
|
|
|
|
|
It is clear that if the C code is implemented using the existing capabilities
|
|
|
|
|
to implement positional-only parameters using the argument clinic and
|
|
|
|
|
related machinery, it is not possible for the pure Python counterpart
|
|
|
|
|
to match the provided interface and requirements. This also creates a disparity
|
|
|
|
|
between the interfaces of some functions and classes in the CPython standard
|
|
|
|
|
library and other Python implementations. For example::
|
|
|
|
|
|
|
|
|
|
$ python3 # CPython 3.7.2
|
|
|
|
|
>>> import binascii; binascii.crc32(data=b'data')
|
|
|
|
|
TypeError: crc32() takes no keyword arguments
|
|
|
|
|
|
|
|
|
|
$ pypy3 # PyPy 6.0.0
|
|
|
|
|
>>>> import binascii; binascii.crc32(data=b'data')
|
|
|
|
|
2918445923
|
|
|
|
|
|
|
|
|
|
Other Python implementations can, of course, reproduce the CPython APIs
|
|
|
|
|
manually, but this goes against the spirit of PEP 399 [#PEP399]_ that
|
|
|
|
|
intends to avoid duplication of effort by mandating that all modules added
|
|
|
|
|
to Python's standard library **must** have a pure Python implementation
|
|
|
|
|
with the same interface and semantics.
|
|
|
|
|
|
|
|
|
|
A final argument in favor of positional-only arguments is that they allow
|
|
|
|
|
some new optimizations like the ones already present in the argument clinic
|
|
|
|
|
since said parameters must be passed in strict order. For instance, CPython's
|
|
|
|
|
internal *METH_FASTCALL* calling convention has been recently speciallized
|
|
|
|
|
for functions with positional-only parameters to eliminate the cost for
|
|
|
|
|
handling empty keywords. Similar performance improvements can be
|
|
|
|
|
applied when creating the evaluation frame of Python functions thanks to
|
|
|
|
|
positional-only parameters.
|
|
|
|
|
|
2018-02-01 14:02:37 -05:00
|
|
|
|
=================================================================
|
|
|
|
|
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
|
2019-03-11 19:16:05 -04:00
|
|
|
|
there is no way to tell whether a function takes positional-only
|
2018-02-01 14:02:37 -05:00
|
|
|
|
parameters. ``open()`` accepts keyword arguments, ``ord()`` does
|
|
|
|
|
not, but there is no way of telling just by reading the
|
2019-03-11 19:16:05 -04:00
|
|
|
|
documentation.
|
2018-02-01 14:02:37 -05:00
|
|
|
|
|
|
|
|
|
====================
|
|
|
|
|
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-03-27 17:03:51 -04:00
|
|
|
|
The logic around optional values for positional-only arguments
|
|
|
|
|
remains the same as for positional-or-keyword arguments. Once
|
2018-02-01 14:02:37 -05:00
|
|
|
|
a positional-only argument is provided with a default,
|
2019-03-27 17:03:51 -04:00
|
|
|
|
the following positional-only and positional-or-keyword arguments
|
|
|
|
|
need to have defaults as well. Positional-only parameters that
|
|
|
|
|
do not have a default values are *required* positional-only parameters.
|
2018-02-01 14:02:37 -05:00
|
|
|
|
Therefore the following are valid signatures::
|
|
|
|
|
|
|
|
|
|
def name(p1, p2, /, p_or_kw, *, kw):
|
|
|
|
|
def name(p1, p2=None, /, p_or_kw=None, *, kw):
|
|
|
|
|
def name(p1, p2=None, /, *, kw):
|
|
|
|
|
def name(p1, p2=None, /):
|
|
|
|
|
def name(p1, p2, /, p_or_kw):
|
|
|
|
|
def name(p1, p2, /):
|
|
|
|
|
|
2019-03-27 17:03:51 -04:00
|
|
|
|
While the followings are not::
|
2018-02-01 14:02:37 -05:00
|
|
|
|
|
|
|
|
|
def name(p1, p2=None, /, p_or_kw, *, kw):
|
|
|
|
|
def name(p1=None, p2, /, p_or_kw=None, *, kw):
|
|
|
|
|
def name(p1=None, p2, /):
|
|
|
|
|
|
2019-03-27 17:03:51 -04:00
|
|
|
|
--------------------------------
|
|
|
|
|
Origin of the "/" as a separator
|
|
|
|
|
--------------------------------
|
|
|
|
|
|
|
|
|
|
Using the "/" as a separator was initially proposed by Guido van Rossum
|
|
|
|
|
in 2012 [#GUIDO]_ :
|
|
|
|
|
|
|
|
|
|
Alternative proposal: how about using '/' ? It's kind of the opposite
|
|
|
|
|
of '*' which means "keyword argument", and '/' is not a new character.
|
|
|
|
|
|
2018-02-01 14:02:37 -05:00
|
|
|
|
==========================
|
|
|
|
|
Full grammar specification
|
|
|
|
|
==========================
|
|
|
|
|
|
|
|
|
|
A draft of the proposed grammar specification is::
|
|
|
|
|
|
|
|
|
|
new_typedargslist:
|
2019-03-05 16:59:06 -05:00
|
|
|
|
tfpdef ['=' test] (',' tfpdef ['=' test])* ',' '/' [',' [typedargslist]] | typedargslist
|
2018-02-01 14:02:37 -05:00
|
|
|
|
|
|
|
|
|
new_varargslist:
|
2019-03-05 16:59:06 -05:00
|
|
|
|
vfpdef ['=' test] (',' vfpdef ['=' test])* ',' '/' [',' [varargslist]] | varargslist
|
2018-02-01 14:02:37 -05:00
|
|
|
|
|
2019-03-27 17:03:51 -04:00
|
|
|
|
It will be added to the actual ``typedargslist`` and ``varargslist``, but for
|
|
|
|
|
more relaxed discussion it is presented as ``new_typedargslist`` and
|
|
|
|
|
``new_varargslist``. Also, notice that using a construction with two new rules
|
|
|
|
|
(new_varargslist and new_varargslist) is not possible with the current parser
|
|
|
|
|
as a rule is not LL(1). This is the reason the rule needs to be included in
|
|
|
|
|
the existing typedargslist and varargslist (in the same way keyword-only
|
|
|
|
|
arguments were introduced).
|
2018-02-01 14:02:37 -05:00
|
|
|
|
|
|
|
|
|
|
2019-03-05 16:59:06 -05:00
|
|
|
|
==============
|
|
|
|
|
Implementation
|
|
|
|
|
==============
|
2018-02-01 14:02:37 -05:00
|
|
|
|
|
2019-03-05 16:59:06 -05:00
|
|
|
|
An initial implementation that passes the CPython test suite is available
|
|
|
|
|
for evaluation [#posonly-impl]_.
|
2018-02-01 14:02:37 -05:00
|
|
|
|
|
|
|
|
|
The advantages of this implementation involve speed, consistency with the
|
2019-03-05 16:59:06 -05:00
|
|
|
|
implementation of keyword-only parameters as in PEP 3102 and a simpler
|
|
|
|
|
implementation of all the tools and modules that will be impacted by
|
|
|
|
|
this change.
|
2018-02-01 14:02:37 -05:00
|
|
|
|
|
|
|
|
|
==============
|
|
|
|
|
Rejected Ideas
|
|
|
|
|
==============
|
|
|
|
|
|
|
|
|
|
----------
|
|
|
|
|
Do Nothing
|
|
|
|
|
----------
|
|
|
|
|
|
|
|
|
|
Always an option, just not adding it. It was considered
|
|
|
|
|
though that the benefits of adding it is worth the complexity
|
|
|
|
|
it adds to the language.
|
|
|
|
|
|
|
|
|
|
---------------------
|
|
|
|
|
After marker proposal
|
|
|
|
|
---------------------
|
|
|
|
|
|
|
|
|
|
A complaint against the proposal is the fact that the modifier of
|
2019-03-11 19:16:05 -04:00
|
|
|
|
the signature impacts the tokens already passed.
|
2018-02-01 14:02:37 -05:00
|
|
|
|
|
2019-03-11 19:16:05 -04:00
|
|
|
|
This might make it confusing to users to read functions
|
2018-02-01 14:02:37 -05:00
|
|
|
|
with many arguments. Example::
|
|
|
|
|
|
|
|
|
|
def really_bad_example_of_a_python_function(fist_long_argument, second_long_argument,
|
|
|
|
|
third_long_argument, /):
|
|
|
|
|
|
2019-03-11 19:16:05 -04:00
|
|
|
|
It is not until reaching the end of the signature that the reader
|
2019-03-27 17:03:51 -04:00
|
|
|
|
realises the ``/``, and therefore the fact that the arguments are
|
2018-02-01 14:02:37 -05:00
|
|
|
|
position-only. This deviates from how the keyword-only marker works.
|
|
|
|
|
|
|
|
|
|
That said we could not find an implementation that would modify the
|
|
|
|
|
arguments after the marker, as that will force the one before the
|
2019-03-11 19:16:05 -04:00
|
|
|
|
marker to be position-only as well. Example::
|
2018-02-01 14:02:37 -05:00
|
|
|
|
|
|
|
|
|
def (x, y, /, z):
|
|
|
|
|
|
2019-03-27 17:03:51 -04:00
|
|
|
|
If we define that ``/`` makes only z position-only, it will not be possible
|
2018-02-01 14:02:37 -05:00
|
|
|
|
to call x and y via keyword argument. Finding a way to work around it
|
|
|
|
|
will add confusion given that at the moment keyword arguments cannot be
|
2019-03-27 17:03:51 -04:00
|
|
|
|
followed by positional arguments. ``/`` will, therefore, make both the
|
2019-03-11 19:16:05 -04:00
|
|
|
|
preceding and following parameters position-only.
|
2018-02-01 14:02:37 -05:00
|
|
|
|
|
|
|
|
|
-------------------
|
|
|
|
|
Per-argument marker
|
|
|
|
|
-------------------
|
|
|
|
|
|
2019-03-27 17:03:51 -04:00
|
|
|
|
Using a per-argument marker might be an option as well. The approach adds a
|
|
|
|
|
token to each of the arguments that are position only and requires those to be
|
|
|
|
|
placed together. Example::
|
2018-02-01 14:02:37 -05:00
|
|
|
|
|
|
|
|
|
def (.arg1, .arg2, arg3):
|
|
|
|
|
|
|
|
|
|
Note the dot on arg1 and arg2. Even if this approach might look easier
|
2019-03-11 19:16:05 -04:00
|
|
|
|
to read, it has been discarded as ``/`` goes further in line with the
|
|
|
|
|
keyword-only approach and is less error-prone.
|
2018-02-01 14:02:37 -05:00
|
|
|
|
|
2019-03-27 17:03:51 -04:00
|
|
|
|
Some libraries use leading underscore [#leading-underscore]_
|
2019-03-11 19:16:05 -04:00
|
|
|
|
to mark those arguments as positional-only.
|
2018-02-01 14:02:37 -05:00
|
|
|
|
|
|
|
|
|
----------------
|
|
|
|
|
Using decorators
|
|
|
|
|
----------------
|
|
|
|
|
|
2019-03-11 19:16:05 -04:00
|
|
|
|
It has been suggested on python-ideas [#python-ideas-decorator-based]_ to
|
|
|
|
|
provide a decorator written in Python as an implementation for this feature.
|
|
|
|
|
This approach has the advantage that keeps parameter declaration more easy to
|
|
|
|
|
read but also introduces an asymmetry on how parameter behaviour is declared.
|
|
|
|
|
Also, as the ``/`` syntax is already introduced for C functions, this
|
|
|
|
|
inconsistency will make it more difficult to implement all tools and modules
|
|
|
|
|
that deal with this syntax including but not limited to, the argument clinic,
|
|
|
|
|
the inspect module and the ast module. Another disadvantage of this approach
|
|
|
|
|
is that calling the decorated functions will be slower than the functions
|
|
|
|
|
generated if the feature was implemented directly in C.
|
2018-02-01 14:02:37 -05:00
|
|
|
|
|
|
|
|
|
======
|
|
|
|
|
Thanks
|
|
|
|
|
======
|
|
|
|
|
|
2019-03-27 17:03:51 -04:00
|
|
|
|
Credit for some of the content of this PEP is contained in Larry Hastings’s
|
2019-03-11 19:16:05 -04:00
|
|
|
|
PEP 457.
|
2018-02-01 14:02:37 -05:00
|
|
|
|
|
2019-03-11 19:16:05 -04:00
|
|
|
|
Credit for the use of '/' as the separator between positional-only and
|
|
|
|
|
positional-or-keyword parameters go to Guido van Rossum, in a proposal from
|
|
|
|
|
2012. [#GUIDO]_
|
2018-02-01 14:02:37 -05:00
|
|
|
|
|
|
|
|
|
Credit for discussion about the simplification of the grammar goes to
|
|
|
|
|
Braulio Valdivieso.
|
|
|
|
|
|
2019-03-27 17:03:51 -04:00
|
|
|
|
|
|
|
|
|
.. [#numpy-ufuncs]
|
|
|
|
|
https://docs.scipy.org/doc/numpy/reference/ufuncs.html#available-ufuncs
|
|
|
|
|
|
|
|
|
|
.. [#scipy-gammaln]
|
|
|
|
|
https://docs.scipy.org/doc/scipy/reference/generated/scipy.special.gammaln.html
|
|
|
|
|
|
2018-02-01 14:02:37 -05:00
|
|
|
|
.. [#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
|
|
|
|
|
|
2019-03-11 11:13:46 -04:00
|
|
|
|
.. [#PEP399]
|
|
|
|
|
https://www.python.org/dev/peps/pep-0399/
|
|
|
|
|
|
2018-02-01 14:02:37 -05:00
|
|
|
|
.. [#python-ideas-decorator-based]
|
|
|
|
|
https://mail.python.org/pipermail/python-ideas/2017-February/044888.html
|
|
|
|
|
|
2019-03-05 16:59:06 -05:00
|
|
|
|
.. [#posonly-impl]
|
|
|
|
|
https://github.com/pablogsal/cpython_positional_only
|
|
|
|
|
|
2019-03-11 11:13:46 -04:00
|
|
|
|
.. [#thread-keyword-to-positional]
|
|
|
|
|
https://mail.python.org/pipermail/python-ideas/2016-January/037874.html
|
|
|
|
|
|
|
|
|
|
.. [#leading-underscore]
|
|
|
|
|
https://mail.python.org/pipermail/python-ideas/2018-September/053319.html
|
|
|
|
|
|
2019-03-27 17:03:51 -04:00
|
|
|
|
.. [#document-positional-only]
|
|
|
|
|
https://bugs.python.org/issue21314
|
|
|
|
|
|
2018-02-01 14:02:37 -05:00
|
|
|
|
=========
|
|
|
|
|
Copyright
|
|
|
|
|
=========
|
|
|
|
|
|
|
|
|
|
This document has been placed in the public domain.
|