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>,
|
2020-06-04 05:37:58 -04:00
|
|
|
|
Pablo Galindo <pablogsal@python.org>,
|
2019-04-03 11:31:33 -04:00
|
|
|
|
Mario Corchero <mariocj89@gmail.com>,
|
|
|
|
|
Eric N. Vander Weele <ericvw@gmail.com>
|
2019-03-28 19:22:14 -04:00
|
|
|
|
BDFL-Delegate: Guido van Rossum <guido@python.org>
|
|
|
|
|
Discussions-To: https://discuss.python.org/t/pep-570-python-positional-only-parameters/1078
|
2022-03-29 19:24:06 -04:00
|
|
|
|
Status: Final
|
2018-02-01 14:02:37 -05:00
|
|
|
|
Type: Standards Track
|
|
|
|
|
Content-Type: text/x-rst
|
|
|
|
|
Created: 20-Jan-2018
|
2020-06-01 16:31:07 -04:00
|
|
|
|
Python-Version: 3.8
|
2018-02-01 14:02:37 -05:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
========
|
2019-04-03 11:31:33 -04:00
|
|
|
|
Abstract
|
2018-02-01 14:02:37 -05:00
|
|
|
|
========
|
|
|
|
|
|
2019-04-03 11:31:33 -04:00
|
|
|
|
This PEP proposes to introduce a new syntax, ``/``, for specifying
|
2019-04-05 15:25:17 -04:00
|
|
|
|
positional-only parameters in Python function definitions.
|
2019-04-03 11:31:33 -04:00
|
|
|
|
|
|
|
|
|
Positional-only parameters have no externally-usable name. When a function
|
|
|
|
|
accepting positional-only parameters is called, positional arguments are mapped
|
2019-04-05 15:25:17 -04:00
|
|
|
|
to these parameters based solely on their order.
|
|
|
|
|
|
2019-04-05 18:09:18 -04:00
|
|
|
|
When designing APIs (application programming interfaces), library
|
2019-04-05 15:25:17 -04:00
|
|
|
|
authors try to ensure correct and intended usage of an API. Without the ability to
|
2019-04-05 18:09:18 -04:00
|
|
|
|
specify which parameters are positional-only, library authors must be careful
|
|
|
|
|
when choosing appropriate parameter names. This care must be taken
|
|
|
|
|
even for required parameters or when the parameters
|
2019-04-05 15:25:17 -04:00
|
|
|
|
have no external semantic meaning for callers of the API.
|
|
|
|
|
|
|
|
|
|
In this PEP, we discuss:
|
|
|
|
|
|
|
|
|
|
* Python's history and current semantics for positional-only parameters
|
|
|
|
|
* the problems encountered by not having them
|
|
|
|
|
* how these problems are handled without language-intrinsic support for
|
|
|
|
|
positional-only parameters
|
|
|
|
|
* the benefits of having positional-only parameters
|
|
|
|
|
|
|
|
|
|
Within context of the motivation, we then:
|
|
|
|
|
|
|
|
|
|
* discuss why positional-only parameters should be a feature intrinsic to the
|
|
|
|
|
language
|
2019-04-05 18:09:18 -04:00
|
|
|
|
* propose the syntax for marking positional-only parameters
|
2019-04-05 15:25:17 -04:00
|
|
|
|
* present how to teach this new feature
|
|
|
|
|
* note rejected ideas in further detail
|
2018-02-01 14:02:37 -05:00
|
|
|
|
|
2019-04-03 11:31:33 -04:00
|
|
|
|
==========
|
|
|
|
|
Motivation
|
|
|
|
|
==========
|
|
|
|
|
|
|
|
|
|
--------------------------------------------------------
|
|
|
|
|
History of Positional-Only Parameter Semantics in Python
|
|
|
|
|
--------------------------------------------------------
|
|
|
|
|
|
|
|
|
|
Python originally supported positional-only parameters. Early versions of the
|
2019-04-05 15:25:17 -04:00
|
|
|
|
language lacked the ability to call functions with arguments bound to parameters
|
2019-04-03 11:31:33 -04:00
|
|
|
|
by name. Around Python 1.0, parameter semantics changed to be
|
2019-04-05 18:09:18 -04:00
|
|
|
|
positional-or-keyword. Since then, users have been able to provide arguments
|
2019-04-03 11:31:33 -04:00
|
|
|
|
to a function either positionally or by the keyword name specified in the
|
|
|
|
|
function's definition.
|
|
|
|
|
|
|
|
|
|
In current versions of Python, many CPython "builtin" and standard library
|
2019-04-05 15:25:17 -04:00
|
|
|
|
functions only accept positional-only parameters. The resulting semantics can be
|
2019-04-03 11:31:33 -04:00
|
|
|
|
easily observed by calling one of these functions using keyword arguments::
|
|
|
|
|
|
|
|
|
|
>>> help(pow)
|
|
|
|
|
...
|
|
|
|
|
pow(x, y, z=None, /)
|
|
|
|
|
...
|
2019-04-05 15:25:17 -04:00
|
|
|
|
|
2019-04-03 11:31:33 -04:00
|
|
|
|
>>> pow(x=5, y=3)
|
|
|
|
|
Traceback (most recent call last):
|
|
|
|
|
File "<stdin>", line 1, in <module>
|
|
|
|
|
TypeError: pow() takes no keyword arguments
|
|
|
|
|
|
2019-04-05 15:25:17 -04:00
|
|
|
|
``pow()`` expresses that its parameters are positional-only via the
|
2019-04-03 11:31:33 -04:00
|
|
|
|
``/`` marker. However, this is only a documentation convention; Python
|
|
|
|
|
developers cannot use this syntax in code.
|
|
|
|
|
|
2019-04-05 15:25:17 -04:00
|
|
|
|
There are functions with other interesting semantics:
|
2019-04-03 11:31:33 -04:00
|
|
|
|
|
|
|
|
|
* ``range()``, an overloaded function, 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]_
|
|
|
|
|
|
2019-04-05 15:25:17 -04:00
|
|
|
|
One can emulate these semantics in Python code by accepting
|
2019-04-03 11:31:33 -04:00
|
|
|
|
``(*args, **kwargs)`` and parsing the arguments manually. However, this results
|
|
|
|
|
in a disconnect between the function definition and what the function
|
2019-04-05 15:25:17 -04:00
|
|
|
|
contractually accepts. The function definition does not match the logic of the
|
2019-04-03 11:31:33 -04:00
|
|
|
|
argument handling.
|
|
|
|
|
|
|
|
|
|
Additionally, the ``/`` syntax is used beyond CPython for specifying similar
|
|
|
|
|
semantics (i.e., [#numpy-ufuncs]_ [#scipy-gammaln]_); thus, indicating that
|
|
|
|
|
these scenarios are not exclusive to CPython and the standard library.
|
|
|
|
|
|
|
|
|
|
-------------------------------------------
|
|
|
|
|
Problems Without Positional-Only Parameters
|
|
|
|
|
-------------------------------------------
|
|
|
|
|
|
|
|
|
|
Without positional-only parameters, there are challenges for library authors
|
2019-04-05 18:09:18 -04:00
|
|
|
|
and users of APIs. The following subsections outline the problems
|
2019-04-03 11:31:33 -04:00
|
|
|
|
encountered by each entity.
|
|
|
|
|
|
|
|
|
|
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
|
|
|
Challenges for Library Authors
|
|
|
|
|
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
|
|
|
|
|
|
|
|
With positional-or-keyword parameters, the mix of calling conventions is not
|
|
|
|
|
always desirable. Authors may want to restrict usage of an API by disallowing
|
2019-04-05 15:25:17 -04:00
|
|
|
|
calling the API with keyword arguments, which exposes the name of the parameter when
|
|
|
|
|
part of the public API. This approach is especially useful for required function
|
|
|
|
|
parameters that already have semantic meaning (e.g,
|
2019-04-03 11:31:33 -04:00
|
|
|
|
``namedtuple(typenames, field_names, …)`` or when the parameter name has no
|
|
|
|
|
true external meaning (e.g., ``arg1``, ``arg2``, …, etc for ``min()``). If a
|
2019-04-05 15:25:17 -04:00
|
|
|
|
caller of an API starts using a keyword argument, the library author cannot rename
|
|
|
|
|
the parameter because it would be a breaking change.
|
2019-04-03 11:31:33 -04:00
|
|
|
|
|
|
|
|
|
Positional-only parameters can be emulated by extracting arguments from
|
|
|
|
|
``*args`` one by one. However, this approach is error-prone and is not
|
|
|
|
|
synonymous with the function definition, as previously mentioned. The usage of
|
2019-04-05 18:09:18 -04:00
|
|
|
|
the function is ambiguous and forces users to look at ``help()``, the
|
2019-04-05 15:25:17 -04:00
|
|
|
|
associated auto-generated documentation, or source code to understand what
|
|
|
|
|
parameters the function contractually accepts.
|
2019-04-03 11:31:33 -04:00
|
|
|
|
|
2019-04-05 18:09:18 -04:00
|
|
|
|
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
|
|
|
Challenges for Users of an API
|
|
|
|
|
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
2019-04-03 11:31:33 -04:00
|
|
|
|
|
2019-04-05 18:09:18 -04:00
|
|
|
|
Users may be surprised when first encountering positional-only notation. This
|
2019-04-05 15:25:17 -04:00
|
|
|
|
is expected given that it has only recently been documented
|
2019-04-03 11:31:33 -04:00
|
|
|
|
[#document-positional-only]_ and it is not possible to use in Python code. For
|
|
|
|
|
these reasons, this notation is currently an outlier that appears only in
|
2019-04-05 18:09:18 -04:00
|
|
|
|
CPython APIs developed in C. Documenting the notation and making it possible
|
|
|
|
|
to use it in Python code would eliminate this disconnect.
|
2019-04-03 11:31:33 -04:00
|
|
|
|
|
2019-04-05 15:25:17 -04:00
|
|
|
|
Furthermore, the current documentation for positional-only parameters is inconsistent:
|
2019-04-03 11:31:33 -04:00
|
|
|
|
|
2019-04-05 15:25:17 -04:00
|
|
|
|
* Some functions denote optional groups of positional-only parameters by
|
2019-04-03 11:31:33 -04:00
|
|
|
|
enclosing them in nested square brackets. [#BORDER]_
|
2018-02-01 14:02:37 -05:00
|
|
|
|
|
2019-04-05 15:25:17 -04:00
|
|
|
|
* Some functions denote optional groups of positional-only parameters by
|
|
|
|
|
presenting multiple prototypes with varying numbers of parameters.
|
2019-04-03 11:31:33 -04:00
|
|
|
|
[#SENDFILE]_
|
|
|
|
|
|
|
|
|
|
* Some functions use *both* of the above approaches. [#RANGE]_ [#ADDCH]_
|
|
|
|
|
|
2019-04-05 18:09:18 -04:00
|
|
|
|
Another point the current documentation does not distinguish is
|
2019-04-03 11:31:33 -04:00
|
|
|
|
whether a function takes positional-only parameters. ``open()`` accepts keyword
|
|
|
|
|
arguments; however, ``ord()`` does not — there is no way of telling just by
|
2019-04-05 15:25:17 -04:00
|
|
|
|
reading the existing documentation.
|
2019-04-03 11:31:33 -04:00
|
|
|
|
|
|
|
|
|
--------------------------------------
|
|
|
|
|
Benefits of Positional-Only Parameters
|
|
|
|
|
--------------------------------------
|
|
|
|
|
|
2019-04-05 15:25:17 -04:00
|
|
|
|
Positional-only parameters give more control to library authors to better
|
2019-04-03 11:31:33 -04:00
|
|
|
|
express the intended usage of an API and allows the API to evolve in a safe,
|
|
|
|
|
backward-compatible way. Additionally, it makes the Python language more
|
2019-04-05 15:25:17 -04:00
|
|
|
|
consistent with existing documentation and the behavior of various
|
2019-04-03 11:31:33 -04:00
|
|
|
|
"builtin" and standard library functions.
|
|
|
|
|
|
|
|
|
|
^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
|
|
|
Empowering Library Authors
|
|
|
|
|
^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
|
|
|
|
2019-04-05 15:25:17 -04:00
|
|
|
|
Library authors would have the flexibility to change the name of
|
|
|
|
|
positional-only parameters without breaking callers. This flexibility reduces the
|
2019-04-03 11:31:33 -04:00
|
|
|
|
cognitive burden for choosing an appropriate public-facing name for required
|
|
|
|
|
parameters or parameters that have no true external semantic meaning.
|
|
|
|
|
|
2019-04-05 15:25:17 -04:00
|
|
|
|
Positional-only parameters are useful in several situations such as:
|
|
|
|
|
|
|
|
|
|
* when a function accepts any keyword argument but also can accept a positional one
|
|
|
|
|
* when a parameter has no external semantic meaning
|
|
|
|
|
* when an API's parameters are required and unambiguous
|
|
|
|
|
|
|
|
|
|
A key
|
2019-04-03 11:31:33 -04:00
|
|
|
|
scenario is when a function accepts any keyword argument but can also accepts a
|
|
|
|
|
positional one. Prominent examples are ``Formatter.format`` and
|
|
|
|
|
``dict.update``. For instance, ``dict.update`` accepts a dictionary
|
|
|
|
|
(positionally), an iterable of key/value pairs (positionally), or multiple
|
|
|
|
|
keyword arguments. In this scenario, if the dictionary parameter were not
|
|
|
|
|
positional-only, the user could not use the name that the function definition
|
2019-04-05 15:25:17 -04:00
|
|
|
|
uses for the parameter or, conversely, the function could not distinguish
|
2019-04-03 11:31:33 -04:00
|
|
|
|
easily if the argument received is the dictionary/iterable or a keyword
|
|
|
|
|
argument for updating the key/value pair.
|
|
|
|
|
|
|
|
|
|
Another scenario where positional-only parameters are useful is when the
|
|
|
|
|
parameter name has no true external 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-04-03 11:31:33 -04:00
|
|
|
|
The name of the parameter provides no intrinsic value and forces the API author
|
|
|
|
|
to maintain its name forever since callers might pass ``x`` as a keyword
|
|
|
|
|
argument.
|
2019-03-11 11:13:46 -04:00
|
|
|
|
|
2019-04-05 15:25:17 -04:00
|
|
|
|
Additionally, positional-only parameters are useful when an API's parameters
|
2019-04-03 11:31:33 -04:00
|
|
|
|
are required and is unambiguous with respect to function. For example::
|
2019-03-27 17:03:51 -04:00
|
|
|
|
|
|
|
|
|
def add_to_queue(item: QueueItem):
|
|
|
|
|
...
|
|
|
|
|
|
2019-04-05 15:25:17 -04:00
|
|
|
|
The name of the function makes clear the argument expected. A keyword
|
2019-04-03 11:31:33 -04:00
|
|
|
|
argument provides minimal benefit and also limits the 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::
|
2019-03-27 17:03:51 -04:00
|
|
|
|
|
|
|
|
|
def add_to_queue(items: Union[QueueItem, List[QueueItem]]):
|
|
|
|
|
...
|
|
|
|
|
|
|
|
|
|
or to take them by using argument lists::
|
|
|
|
|
|
|
|
|
|
def add_to_queue(*items: QueueItem):
|
|
|
|
|
...
|
|
|
|
|
|
2019-04-03 11:31:33 -04:00
|
|
|
|
the author would be forced to always keep the original parameter name to avoid
|
2019-04-05 15:25:17 -04:00
|
|
|
|
potentially breaking callers.
|
2019-04-03 11:31:33 -04:00
|
|
|
|
|
2019-04-05 15:25:17 -04:00
|
|
|
|
By being able to specify positional-only parameters, an author can change the
|
2019-04-03 11:31:33 -04:00
|
|
|
|
name of the parameters freely or even change them to ``*args``, as seen in the
|
|
|
|
|
previous example. There are multiple function definitions in the standard
|
|
|
|
|
library which fall into this category. For example, the required parameter to
|
|
|
|
|
``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 undesirable that a caller can bind by
|
|
|
|
|
keyword to the name ``self`` when calling the method from the class::
|
2019-03-27 17:03:51 -04:00
|
|
|
|
|
|
|
|
|
io.FileIO.write(self=f, b=b"data")
|
|
|
|
|
|
2019-04-03 11:31:33 -04:00
|
|
|
|
Indeed, function definitions from the standard library implemented in C usually
|
2019-04-05 15:25:17 -04:00
|
|
|
|
take ``self`` as a positional-only parameter::
|
2019-03-27 17:03:51 -04:00
|
|
|
|
|
|
|
|
|
>>> 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-04-03 11:31:33 -04:00
|
|
|
|
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
|
|
|
Improving Language Consistency
|
|
|
|
|
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
|
|
|
|
2019-04-05 18:09:18 -04:00
|
|
|
|
The Python language would be more consistent with positional-only
|
2019-04-03 11:31:33 -04:00
|
|
|
|
parameters. If the concept is a normal feature of Python rather than a feature
|
|
|
|
|
exclusive to extension modules, it would reduce confusion for users
|
2019-04-05 15:25:17 -04:00
|
|
|
|
encountering functions with positional-only parameters. Some major
|
2019-04-03 11:31:33 -04:00
|
|
|
|
third-party packages are already using the ``/`` notation in their function
|
|
|
|
|
definitions [#numpy-ufuncs]_ [#scipy-gammaln]_.
|
|
|
|
|
|
2019-04-05 15:25:17 -04:00
|
|
|
|
Bridging the gap found between "builtin" functions which
|
2019-04-03 11:31:33 -04:00
|
|
|
|
specify positional-only parameters and pure Python implementations that lack
|
2019-04-05 15:25:17 -04:00
|
|
|
|
the positional syntax would improve consistency. The ``/`` syntax is already exposed
|
|
|
|
|
in the existing documentation such as when builtins and interfaces are generated
|
|
|
|
|
by the argument clinic.
|
2019-04-03 11:31:33 -04:00
|
|
|
|
|
2022-01-21 06:03:51 -05:00
|
|
|
|
Another essential aspect to consider is :pep:`399`, which mandates that
|
2019-04-03 11:31:33 -04:00
|
|
|
|
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.
|
2018-02-01 14:02:37 -05:00
|
|
|
|
|
2019-04-03 11:31:33 -04:00
|
|
|
|
=========
|
|
|
|
|
Rationale
|
|
|
|
|
=========
|
2018-02-01 14:02:37 -05:00
|
|
|
|
|
2019-04-03 11:31:33 -04:00
|
|
|
|
We propose to introduce positional-only parameters as a new syntax to the
|
|
|
|
|
Python language.
|
2018-02-01 14:02:37 -05:00
|
|
|
|
|
2019-04-05 18:09:18 -04:00
|
|
|
|
The new syntax will enable library authors to further control how their API
|
|
|
|
|
can be called. It will allow designating which parameters must be called as
|
2019-04-05 15:25:17 -04:00
|
|
|
|
positional-only, while preventing them from being called as keyword arguments.
|
2018-02-01 14:02:37 -05:00
|
|
|
|
|
2022-01-21 06:03:51 -05:00
|
|
|
|
Previously, (informational) :pep:`457` defined the syntax, but with a much more vague
|
2019-04-05 15:25:17 -04:00
|
|
|
|
scope. This PEP takes the original proposal a step further by justifying
|
2019-04-03 11:31:33 -04:00
|
|
|
|
the syntax and providing an implementation for the ``/`` syntax in function
|
|
|
|
|
definitions.
|
2019-03-27 17:03:51 -04:00
|
|
|
|
|
2019-04-05 15:25:17 -04:00
|
|
|
|
-----------
|
|
|
|
|
Performance
|
|
|
|
|
-----------
|
|
|
|
|
|
2019-04-03 11:31:33 -04:00
|
|
|
|
In addition to the aforementioned benefits, the parsing and handling of
|
|
|
|
|
positional-only arguments is faster. This performance benefit can be
|
|
|
|
|
demonstrated in this thread about converting keyword arguments to positional:
|
|
|
|
|
[#thread-keyword-to-positional]_. Due to this speedup, there has 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``.
|
2018-02-01 14:02:37 -05:00
|
|
|
|
|
2019-04-05 15:25:17 -04:00
|
|
|
|
---------------
|
|
|
|
|
Maintainability
|
|
|
|
|
---------------
|
|
|
|
|
|
2019-04-05 18:09:18 -04:00
|
|
|
|
Providing a way to specify positional-only parameters in Python will make it
|
2019-04-03 11:31:33 -04:00
|
|
|
|
easier to maintain pure Python implementations of C modules. Additionally,
|
2019-04-05 18:09:18 -04:00
|
|
|
|
library authors defining functions will have the choice for choosing
|
2019-04-03 11:31:33 -04:00
|
|
|
|
positional-only parameters if they determine that passing a keyword argument
|
|
|
|
|
provides no additional clarity.
|
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-04-05 15:25:17 -04:00
|
|
|
|
----------------
|
|
|
|
|
Logical ordering
|
|
|
|
|
----------------
|
|
|
|
|
|
2019-04-03 11:31:33 -04:00
|
|
|
|
Positional-only parameters also have the (minor) benefit of enforcing some
|
|
|
|
|
logical order when calling interfaces that make use of them. For example, the
|
|
|
|
|
``range`` function takes all its parameters positionally and disallows forms
|
|
|
|
|
like::
|
2019-03-27 17:03:51 -04:00
|
|
|
|
|
|
|
|
|
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)
|
|
|
|
|
|
2019-04-05 15:25:17 -04:00
|
|
|
|
-------------------------------------------
|
2019-04-05 18:09:18 -04:00
|
|
|
|
Compatibility for Pure Python and C Modules
|
2019-04-05 15:25:17 -04:00
|
|
|
|
-------------------------------------------
|
|
|
|
|
|
2022-01-21 06:03:51 -05:00
|
|
|
|
Another critical motivation for positional-only parameters is :pep:`399`:
|
2019-04-05 18:09:18 -04:00
|
|
|
|
Pure Python/C Accelerator Module Compatibility Requirements. This
|
2019-04-05 15:25:17 -04:00
|
|
|
|
PEP states that:
|
2019-03-27 17:03:51 -04:00
|
|
|
|
|
2019-04-03 11:31:33 -04:00
|
|
|
|
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
|
2019-03-27 17:03:51 -04:00
|
|
|
|
|
2019-04-05 15:25:17 -04:00
|
|
|
|
If the C code is implemented using the existing capabilities
|
2019-04-03 11:31:33 -04:00
|
|
|
|
to implement positional-only parameters using the argument clinic, and related
|
|
|
|
|
machinery, it is not possible for the pure Python counterpart to match the
|
2019-04-05 15:25:17 -04:00
|
|
|
|
provided interface and requirements. This creates a disparity between the
|
2019-04-03 11:31:33 -04:00
|
|
|
|
interfaces of some functions and classes in the CPython standard library and
|
|
|
|
|
other Python implementations. For example::
|
2019-03-27 17:03:51 -04:00
|
|
|
|
|
|
|
|
|
$ 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
|
|
|
|
|
|
2019-04-03 11:31:33 -04:00
|
|
|
|
Other Python implementations can reproduce the CPython APIs manually, but this
|
2022-01-21 06:03:51 -05:00
|
|
|
|
goes against the spirit of :pep:`399` to avoid duplication of effort by
|
2019-04-03 11:31:33 -04:00
|
|
|
|
mandating that all modules added to Python's standard library **must** have a
|
|
|
|
|
pure Python implementation with the same interface and semantics.
|
2019-03-27 17:03:51 -04:00
|
|
|
|
|
2019-04-05 15:25:17 -04:00
|
|
|
|
-------------------------
|
|
|
|
|
Consistency in Subclasses
|
|
|
|
|
-------------------------
|
|
|
|
|
|
2019-04-03 11:31:33 -04:00
|
|
|
|
Another scenario where positional-only parameters provide benefit occurs when a
|
2019-04-05 15:25:17 -04:00
|
|
|
|
subclass overrides a method of the base class and changes the name of parameters
|
2019-04-03 11:31:33 -04:00
|
|
|
|
that are intended to be positional::
|
2019-04-03 01:08:54 -04:00
|
|
|
|
|
|
|
|
|
class Base:
|
|
|
|
|
def meth(self, arg: int) -> str:
|
|
|
|
|
...
|
|
|
|
|
|
|
|
|
|
class Sub(Base):
|
|
|
|
|
def meth(self, other_arg: int) -> str:
|
|
|
|
|
...
|
|
|
|
|
|
|
|
|
|
def func(x: Base):
|
|
|
|
|
x.meth(arg=12)
|
|
|
|
|
|
|
|
|
|
func(Sub()) # Runtime error
|
|
|
|
|
|
2019-04-03 11:31:33 -04:00
|
|
|
|
This situation could be considered a Liskov violation — the subclass cannot be
|
|
|
|
|
used in a context when an instance of the base class is expected. Renaming
|
|
|
|
|
arguments when overloading methods can happen when the subclass has reasons to
|
|
|
|
|
use a different choice for the parameter name that is more appropriate for the
|
|
|
|
|
specific domain of the subclass (e.g., when subclassing ``Mapping`` to
|
2019-04-03 01:08:54 -04:00
|
|
|
|
implement a DNS lookup cache, the derived class may not want to use the generic
|
2019-04-03 11:31:33 -04:00
|
|
|
|
argument names ‘key’ and ‘value’ but rather ‘host’ and ‘address’). Having this
|
|
|
|
|
function definition with positional-only parameters can avoid this problem
|
|
|
|
|
because users will not be able to call the interface using keyword arguments.
|
|
|
|
|
In general, designing for subclassing usually involves anticipating code that
|
|
|
|
|
hasn't been written yet and over which the author has no control. Having
|
|
|
|
|
measures that can facilitate the evolution of interfaces in a
|
|
|
|
|
backwards-compatible would be useful for library authors.
|
|
|
|
|
|
2019-04-05 15:25:17 -04:00
|
|
|
|
-------------
|
|
|
|
|
Optimizations
|
|
|
|
|
-------------
|
|
|
|
|
|
2019-04-03 11:31:33 -04:00
|
|
|
|
A final argument in favor of positional-only parameters is that they allow some
|
|
|
|
|
new optimizations like the ones already present in the argument clinic due to
|
2019-04-05 15:25:17 -04:00
|
|
|
|
the fact that parameters are expected to be passed in strict order. For example, CPython's
|
2019-04-05 18:09:18 -04:00
|
|
|
|
internal ``METH_FASTCALL`` calling convention has been recently specialized for
|
2019-04-03 11:31:33 -04:00
|
|
|
|
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.
|
|
|
|
|
|
|
|
|
|
=============
|
|
|
|
|
Specification
|
|
|
|
|
=============
|
|
|
|
|
|
|
|
|
|
--------------------
|
|
|
|
|
Syntax and Semantics
|
|
|
|
|
--------------------
|
|
|
|
|
|
|
|
|
|
From the "ten-thousand foot view", eliding ``*args`` and ``**kwargs`` for
|
|
|
|
|
illustration, the grammar for a function definition would look like::
|
2018-02-01 14:02:37 -05:00
|
|
|
|
|
|
|
|
|
def name(positional_or_keyword_parameters, *, keyword_only_parameters):
|
|
|
|
|
|
2019-04-03 11:31:33 -04:00
|
|
|
|
Building on that example, the new syntax for function definitions would look
|
|
|
|
|
like::
|
2018-02-01 14:02:37 -05:00
|
|
|
|
|
|
|
|
|
def name(positional_only_parameters, /, positional_or_keyword_parameters,
|
|
|
|
|
*, keyword_only_parameters):
|
|
|
|
|
|
2019-04-05 15:25:17 -04:00
|
|
|
|
The following would apply:
|
|
|
|
|
|
2019-04-05 18:09:18 -04:00
|
|
|
|
* All parameters left of the ``/`` are treated as positional-only.
|
2019-04-05 15:25:17 -04:00
|
|
|
|
* If ``/`` is not specified in the function definition, that function does not
|
|
|
|
|
accept any positional-only arguments.
|
|
|
|
|
* The logic around optional values for positional-only parameters remains the
|
|
|
|
|
same as for positional-or-keyword parameters.
|
|
|
|
|
* Once a positional-only parameter is specified with a default, the
|
|
|
|
|
following positional-only and positional-or-keyword parameters need to have
|
|
|
|
|
defaults as well.
|
|
|
|
|
* Positional-only parameters which do not have default
|
|
|
|
|
values are *required* positional-only parameters.
|
|
|
|
|
|
2021-02-03 09:06:23 -05:00
|
|
|
|
Therefore, the following would be valid function definitions::
|
2018-02-01 14:02:37 -05:00
|
|
|
|
|
|
|
|
|
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-04-05 15:25:17 -04:00
|
|
|
|
Just like today, the following would be valid function definitions::
|
|
|
|
|
|
|
|
|
|
def name(p_or_kw, *, kw):
|
|
|
|
|
def name(*, kw):
|
|
|
|
|
|
|
|
|
|
While the following would be invalid::
|
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-04-03 11:31:33 -04:00
|
|
|
|
--------------------------
|
2019-04-05 18:09:18 -04:00
|
|
|
|
Full Grammar Specification
|
2019-04-03 11:31:33 -04:00
|
|
|
|
--------------------------
|
2018-02-01 14:02:37 -05:00
|
|
|
|
|
2019-04-05 15:25:17 -04:00
|
|
|
|
A simplified view of the proposed grammar specification is::
|
|
|
|
|
|
|
|
|
|
typedargslist:
|
|
|
|
|
tfpdef ['=' test] (',' tfpdef ['=' test])* ',' '/' [',' # and so on
|
|
|
|
|
|
|
|
|
|
varargslist:
|
|
|
|
|
vfpdef ['=' test] (',' vfpdef ['=' test])* ',' '/' [',' # and so on
|
|
|
|
|
|
|
|
|
|
Based on the reference implementation in this PEP, the new rule for
|
|
|
|
|
``typedarglist`` would be::
|
|
|
|
|
|
|
|
|
|
typedargslist: (tfpdef ['=' test] (',' tfpdef ['=' test])* ',' '/' [',' [tfpdef ['=' test] (',' tfpdef ['=' test])* [',' [
|
|
|
|
|
'*' [tfpdef] (',' tfpdef ['=' test])* [',' ['**' tfpdef [',']]]
|
|
|
|
|
| '**' tfpdef [',']]]
|
|
|
|
|
| '*' [tfpdef] (',' tfpdef ['=' test])* [',' ['**' tfpdef [',']]]
|
|
|
|
|
| '**' tfpdef [',']] ] )| (
|
|
|
|
|
tfpdef ['=' test] (',' tfpdef ['=' test])* [',' [
|
|
|
|
|
'*' [tfpdef] (',' tfpdef ['=' test])* [',' ['**' tfpdef [',']]]
|
|
|
|
|
| '**' tfpdef [',']]]
|
|
|
|
|
| '*' [tfpdef] (',' tfpdef ['=' test])* [',' ['**' tfpdef [',']]]
|
|
|
|
|
| '**' tfpdef [','])
|
|
|
|
|
|
|
|
|
|
and for ``varargslist`` would be::
|
|
|
|
|
|
|
|
|
|
varargslist: vfpdef ['=' test ](',' vfpdef ['=' test])* ',' '/' [',' [ (vfpdef ['=' test] (',' vfpdef ['=' test])* [',' [
|
|
|
|
|
'*' [vfpdef] (',' vfpdef ['=' test])* [',' ['**' vfpdef [',']]]
|
|
|
|
|
| '**' vfpdef [',']]]
|
|
|
|
|
| '*' [vfpdef] (',' vfpdef ['=' test])* [',' ['**' vfpdef [',']]]
|
|
|
|
|
| '**' vfpdef [',']) ]] | (vfpdef ['=' test] (',' vfpdef ['=' test])* [',' [
|
|
|
|
|
'*' [vfpdef] (',' vfpdef ['=' test])* [',' ['**' vfpdef [',']]]
|
|
|
|
|
| '**' vfpdef [',']]]
|
|
|
|
|
| '*' [vfpdef] (',' vfpdef ['=' test])* [',' ['**' vfpdef [',']]]
|
|
|
|
|
| '**' vfpdef [',']
|
|
|
|
|
)
|
2019-04-06 17:43:10 -04:00
|
|
|
|
|
|
|
|
|
--------------------
|
|
|
|
|
Semantic Corner Case
|
|
|
|
|
--------------------
|
|
|
|
|
|
|
|
|
|
The following is an interesting corollary of the specification.
|
|
|
|
|
Consider this function definition::
|
|
|
|
|
|
|
|
|
|
def foo(name, **kwds):
|
|
|
|
|
return 'name' in kwds
|
|
|
|
|
|
|
|
|
|
There is no possible call that will make it return ``True``.
|
|
|
|
|
For example::
|
|
|
|
|
|
|
|
|
|
>>> foo(1, **{'name': 2})
|
|
|
|
|
Traceback (most recent call last):
|
|
|
|
|
File "<stdin>", line 1, in <module>
|
|
|
|
|
TypeError: foo() got multiple values for argument 'name'
|
|
|
|
|
>>>
|
|
|
|
|
|
|
|
|
|
But using ``/`` we can support this::
|
|
|
|
|
|
|
|
|
|
def foo(name, /, **kwds):
|
|
|
|
|
return 'name' in kwds
|
|
|
|
|
|
|
|
|
|
Now the above call will return ``True``.
|
|
|
|
|
|
|
|
|
|
In other words, the names of positional-only parameters can be used in
|
|
|
|
|
``**kwds`` without ambiguity. (As another example, this benefits the
|
|
|
|
|
signatures of ``dict()`` and ``dict.update()``.)
|
2019-04-03 11:31:33 -04:00
|
|
|
|
|
2019-04-05 18:09:18 -04:00
|
|
|
|
----------------------------
|
|
|
|
|
Origin of "/" as a Separator
|
|
|
|
|
----------------------------
|
2019-04-03 11:31:33 -04:00
|
|
|
|
|
2019-04-05 18:09:18 -04:00
|
|
|
|
Using ``/`` as a separator was initially proposed by Guido van Rossum
|
2019-04-03 11:31:33 -04:00
|
|
|
|
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
|
|
|
|
|
2019-04-03 01:08:54 -04:00
|
|
|
|
=================
|
2019-04-05 18:09:18 -04:00
|
|
|
|
How To Teach This
|
2019-04-03 01:08:54 -04:00
|
|
|
|
=================
|
|
|
|
|
|
2019-04-05 15:25:17 -04:00
|
|
|
|
Introducing a dedicated syntax to mark positional-only parameters is closely
|
|
|
|
|
analogous to existing keyword-only arguments. Teaching these concepts together
|
|
|
|
|
may *simplify* how to teach the possible function definitions a user may encounter or
|
|
|
|
|
design.
|
2019-04-03 01:08:54 -04:00
|
|
|
|
|
2019-04-03 11:31:33 -04:00
|
|
|
|
This PEP recommends adding a new subsection to the Python documentation, in the
|
|
|
|
|
section `"More on Defining Functions"`_, where the rest of the argument types
|
2019-04-05 15:25:17 -04:00
|
|
|
|
are discussed. The following paragraphs serve as a draft for these additions.
|
|
|
|
|
They will introduce the notation for both positional-only and
|
|
|
|
|
keyword-only parameters. It is not intended to be exhaustive, nor should it be
|
2019-04-03 11:31:33 -04:00
|
|
|
|
considered the final version to be incorporated into the documentation.
|
2019-04-03 01:08:54 -04:00
|
|
|
|
|
2019-04-05 15:25:17 -04:00
|
|
|
|
|
2019-04-03 01:08:54 -04:00
|
|
|
|
.. _"More on Defining Functions": https://docs.python.org/3.7/tutorial/controlflow.html#more-on-defining-functions
|
|
|
|
|
|
|
|
|
|
-------------------------------------------------------------------------------
|
|
|
|
|
|
2019-04-05 15:25:17 -04:00
|
|
|
|
By default, arguments may be passed to a Python function either by position
|
|
|
|
|
or explicitly by keyword. For readability and performance, it makes sense to
|
|
|
|
|
restrict the way arguments can be passed so that a developer need only look
|
|
|
|
|
at the function definition to determine if items are passed by position, by
|
|
|
|
|
position or keyword, or by keyword.
|
2019-04-03 01:08:54 -04:00
|
|
|
|
|
2019-04-05 15:25:17 -04:00
|
|
|
|
A function definition may look like::
|
2019-04-03 01:08:54 -04:00
|
|
|
|
|
|
|
|
|
def f(pos1, pos2, /, pos_or_kwd, *, kwd1, kwd2):
|
|
|
|
|
----------- ---------- ----------
|
|
|
|
|
| | |
|
|
|
|
|
| Positional or keyword |
|
|
|
|
|
| - Keyword only
|
|
|
|
|
-- Positional only
|
|
|
|
|
|
2019-04-05 15:25:17 -04:00
|
|
|
|
where ``/`` and ``*`` are optional. If used, these symbols indicate the kind of
|
|
|
|
|
parameter by how the arguments may be passed to the function:
|
|
|
|
|
positional-only, positional-or-keyword, and keyword-only. Keyword parameters
|
|
|
|
|
are also referred to as named parameters.
|
|
|
|
|
|
|
|
|
|
-------------------------------
|
|
|
|
|
Positional-or-Keyword Arguments
|
|
|
|
|
-------------------------------
|
|
|
|
|
|
|
|
|
|
If ``/`` and ``*`` are not present in the function definition, arguments may
|
|
|
|
|
be passed to a function by position or by keyword.
|
|
|
|
|
|
|
|
|
|
--------------------------
|
2019-04-05 18:09:18 -04:00
|
|
|
|
Positional-Only Parameters
|
2019-04-05 15:25:17 -04:00
|
|
|
|
--------------------------
|
|
|
|
|
|
|
|
|
|
Looking at this in a bit more detail, it is possible to mark certain parameters
|
|
|
|
|
as *positional-only*. If *positional-only*, the parameters' order matters, and
|
|
|
|
|
the parameters cannot be passed by keyword. Positional-only parameters would
|
|
|
|
|
be placed before a ``/`` (forward-slash). The ``/`` is used to logically
|
|
|
|
|
separate the positional-only parameters from the rest of the parameters.
|
|
|
|
|
If there is no ``/`` in the function definition, there are no positional-only
|
|
|
|
|
parameters.
|
|
|
|
|
|
|
|
|
|
Parameters following the ``/`` may be *positional-or-keyword* or *keyword-only*.
|
|
|
|
|
|
|
|
|
|
----------------------
|
2019-04-05 18:09:18 -04:00
|
|
|
|
Keyword-Only Arguments
|
2019-04-05 15:25:17 -04:00
|
|
|
|
----------------------
|
|
|
|
|
|
|
|
|
|
To mark parameters as *keyword-only*, indicating the parameters must be passed
|
|
|
|
|
by keyword argument, place an ``*`` in the arguments list just before the first
|
|
|
|
|
*keyword-only* parameter.
|
|
|
|
|
|
|
|
|
|
-----------------
|
|
|
|
|
Function Examples
|
|
|
|
|
-----------------
|
|
|
|
|
|
|
|
|
|
Consider the following example function definitions paying close attention to the
|
|
|
|
|
markers ``/`` and ``*``::
|
2019-04-03 01:08:54 -04:00
|
|
|
|
|
|
|
|
|
>>> def standard_arg(arg):
|
|
|
|
|
... print(arg)
|
|
|
|
|
...
|
|
|
|
|
>>> def pos_only_arg(arg, /):
|
|
|
|
|
... print(arg)
|
|
|
|
|
...
|
|
|
|
|
>>> def kwd_only_arg(*, arg):
|
|
|
|
|
... print(arg)
|
|
|
|
|
...
|
|
|
|
|
>>> def combined_example(pos_only, /, standard, *, kwd_only):
|
|
|
|
|
... print(pos_only, standard, kwd_only)
|
|
|
|
|
|
|
|
|
|
|
2019-04-05 15:25:17 -04:00
|
|
|
|
The first function definition ``standard_arg``, the most familiar form,
|
|
|
|
|
places no restrictions on the calling convention and arguments may be
|
|
|
|
|
passed by position or keyword::
|
2019-04-03 01:08:54 -04:00
|
|
|
|
|
|
|
|
|
>>> standard_arg(2)
|
|
|
|
|
2
|
2019-04-05 15:25:17 -04:00
|
|
|
|
|
2019-04-03 01:08:54 -04:00
|
|
|
|
>>> standard_arg(arg=2)
|
|
|
|
|
2
|
|
|
|
|
|
2022-01-21 06:03:51 -05:00
|
|
|
|
The second function ``pos_only_arg`` is restricted to only use positional
|
2019-04-05 15:25:17 -04:00
|
|
|
|
parameters as there is a ``/`` in the function definition::
|
2019-04-03 01:08:54 -04:00
|
|
|
|
|
|
|
|
|
>>> pos_only_arg(1)
|
|
|
|
|
1
|
2019-04-05 15:25:17 -04:00
|
|
|
|
|
2019-04-03 01:08:54 -04:00
|
|
|
|
>>> pos_only_arg(arg=1)
|
|
|
|
|
Traceback (most recent call last):
|
|
|
|
|
File "<stdin>", line 1, in <module>
|
|
|
|
|
TypeError: pos_only_arg() got an unexpected keyword argument 'arg'
|
|
|
|
|
|
2019-04-05 15:25:17 -04:00
|
|
|
|
The third function ``kwd_only_args`` only allows keyword arguments as indicated
|
|
|
|
|
by a ``*`` in the function definition::
|
2019-04-03 01:08:54 -04:00
|
|
|
|
|
|
|
|
|
>>> kwd_only_arg(3)
|
|
|
|
|
Traceback (most recent call last):
|
|
|
|
|
File "<stdin>", line 1, in <module>
|
|
|
|
|
TypeError: kwd_only_arg() takes 0 positional arguments but 1 was given
|
2019-04-05 15:25:17 -04:00
|
|
|
|
|
2019-04-03 01:08:54 -04:00
|
|
|
|
>>> kwd_only_arg(arg=3)
|
|
|
|
|
3
|
|
|
|
|
|
2019-04-03 11:31:33 -04:00
|
|
|
|
And the last uses all three calling conventions in the same function
|
|
|
|
|
definition::
|
2019-04-03 01:08:54 -04:00
|
|
|
|
|
|
|
|
|
>>> combined_example(1, 2, 3)
|
|
|
|
|
Traceback (most recent call last):
|
|
|
|
|
File "<stdin>", line 1, in <module>
|
|
|
|
|
TypeError: combined_example() takes 2 positional arguments but 3 were given
|
2019-04-05 15:25:17 -04:00
|
|
|
|
|
2019-04-03 01:08:54 -04:00
|
|
|
|
>>> combined_example(1, 2, kwd_only=3)
|
|
|
|
|
1 2 3
|
2019-04-05 15:25:17 -04:00
|
|
|
|
|
2019-04-03 01:08:54 -04:00
|
|
|
|
>>> combined_example(1, standard=2, kwd_only=3)
|
|
|
|
|
1 2 3
|
2019-04-05 15:25:17 -04:00
|
|
|
|
|
2019-04-03 01:08:54 -04:00
|
|
|
|
>>> combined_example(pos_only=1, standard=2, kwd_only=3)
|
|
|
|
|
Traceback (most recent call last):
|
|
|
|
|
File "<stdin>", line 1, in <module>
|
|
|
|
|
TypeError: combined_example() got an unexpected keyword argument 'pos_only'
|
|
|
|
|
|
2019-04-05 15:25:17 -04:00
|
|
|
|
-----
|
|
|
|
|
Recap
|
|
|
|
|
-----
|
|
|
|
|
|
|
|
|
|
The use case will determine which parameters to use in the function definition::
|
|
|
|
|
|
|
|
|
|
def f(pos1, pos2, /, pos_or_kwd, *, kwd1, kwd2):
|
|
|
|
|
|
|
|
|
|
As guidance:
|
|
|
|
|
|
|
|
|
|
* Use positional-only if names do not matter or have no meaning, and there are
|
|
|
|
|
only a few arguments which will always be passed in the same order.
|
|
|
|
|
* Use keyword-only when names have meaning and the function definition is
|
|
|
|
|
more understandable by being explicit with names.
|
2018-02-01 14:02:37 -05:00
|
|
|
|
|
2019-04-03 11:31:33 -04:00
|
|
|
|
========================
|
|
|
|
|
Reference Implementation
|
|
|
|
|
========================
|
2018-02-01 14:02:37 -05:00
|
|
|
|
|
2019-04-03 11:31:33 -04:00
|
|
|
|
An initial implementation that passes the CPython test suite is available for
|
|
|
|
|
evaluation [#posonly-impl]_.
|
2018-02-01 14:02:37 -05:00
|
|
|
|
|
2019-04-03 11:31:33 -04:00
|
|
|
|
The benefits of this implementations are speed of handling positional-only
|
|
|
|
|
parameters, consistency with the implementation of keyword-only parameters (PEP
|
|
|
|
|
3102), and a simpler implementation of all the tools and modules that would be
|
|
|
|
|
impacted by this change.
|
2018-02-01 14:02:37 -05:00
|
|
|
|
|
|
|
|
|
==============
|
|
|
|
|
Rejected Ideas
|
|
|
|
|
==============
|
|
|
|
|
|
|
|
|
|
----------
|
|
|
|
|
Do Nothing
|
|
|
|
|
----------
|
|
|
|
|
|
2019-04-03 11:31:33 -04:00
|
|
|
|
Always an option — the status quo. While this was considered, the
|
2019-04-05 15:25:17 -04:00
|
|
|
|
aforementioned benefits are worth the addition to the language.
|
2019-04-03 11:31:33 -04:00
|
|
|
|
|
|
|
|
|
----------
|
|
|
|
|
Decorators
|
|
|
|
|
----------
|
|
|
|
|
|
|
|
|
|
It has been suggested on python-ideas [#python-ideas-decorator-based]_ to
|
|
|
|
|
provide a decorator written in Python for this feature.
|
2018-02-01 14:02:37 -05:00
|
|
|
|
|
2019-04-03 11:31:33 -04:00
|
|
|
|
This approach has the benefit of not polluting function definition with
|
|
|
|
|
additional syntax. However, we have decided to reject this idea because:
|
2018-02-01 14:02:37 -05:00
|
|
|
|
|
2019-04-03 11:31:33 -04:00
|
|
|
|
* It introduces an asymmetry with how parameter behavior is declared.
|
2018-02-01 14:02:37 -05:00
|
|
|
|
|
2019-04-05 15:25:17 -04:00
|
|
|
|
* It makes it difficult for static analyzers and type checkers to
|
2019-04-03 11:31:33 -04:00
|
|
|
|
safely identify positional-only parameters. They would need to query the AST
|
|
|
|
|
for the list of decorators and identify the correct one by name or with extra
|
2019-04-05 15:25:17 -04:00
|
|
|
|
heuristics, while keyword-only parameters are exposed
|
2019-04-03 11:31:33 -04:00
|
|
|
|
directly in the AST. In order for tools to correctly identify
|
|
|
|
|
positional-only parameters, they would need to execute the module to access
|
|
|
|
|
any metadata the decorator is setting.
|
2018-02-01 14:02:37 -05:00
|
|
|
|
|
2019-04-03 11:31:33 -04:00
|
|
|
|
* Any error with the declaration will be reported only at runtime.
|
2018-02-01 14:02:37 -05:00
|
|
|
|
|
2019-04-03 11:31:33 -04:00
|
|
|
|
* It may be more difficult to identify positional-only parameters in long
|
|
|
|
|
function definitions, as it forces the user to count them to know which is
|
|
|
|
|
the last one that is impacted by the decorator.
|
2018-02-01 14:02:37 -05:00
|
|
|
|
|
2019-04-03 11:31:33 -04:00
|
|
|
|
* The ``/`` syntax has already been introduced for C functions. This
|
|
|
|
|
inconsistency will make it more challenging to implement any tools and
|
|
|
|
|
modules that deal with this syntax — including but not limited to, the
|
|
|
|
|
argument clinic, the inspect module and the ``ast`` module.
|
2018-02-01 14:02:37 -05:00
|
|
|
|
|
2019-04-03 11:31:33 -04:00
|
|
|
|
* The decorator implementation would likely impose a runtime performance cost,
|
|
|
|
|
particularly when compared to adding support directly to the interpreter.
|
2018-02-01 14:02:37 -05:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
-------------------
|
2019-04-05 18:09:18 -04:00
|
|
|
|
Per-Argument Marker
|
2018-02-01 14:02:37 -05:00
|
|
|
|
-------------------
|
|
|
|
|
|
2019-04-03 11:31:33 -04:00
|
|
|
|
A per-argument marker is another language-intrinsic option. The approach adds
|
|
|
|
|
a token to each of the parameters to indicate they are positional-only and
|
|
|
|
|
requires those parameters to be placed together. Example::
|
2018-02-01 14:02:37 -05:00
|
|
|
|
|
|
|
|
|
def (.arg1, .arg2, arg3):
|
|
|
|
|
|
2019-04-03 11:31:33 -04:00
|
|
|
|
Note the dot (i.e., ``.``) on ``.arg1`` and ``.arg2``. While this approach
|
|
|
|
|
may be easier to read, it has been rejected because ``/`` as an explicit marker
|
|
|
|
|
is congruent with ``*`` for keyword-only arguments and is less error-prone.
|
|
|
|
|
|
|
|
|
|
It should be noted that some libraries already use leading underscore
|
|
|
|
|
[#leading-underscore]_ to conventionally indicate parameters as positional-only.
|
|
|
|
|
|
|
|
|
|
-----------------------------------
|
2019-04-05 18:09:18 -04:00
|
|
|
|
Using "__" as a Per-Argument Marker
|
2019-04-03 11:31:33 -04:00
|
|
|
|
-----------------------------------
|
|
|
|
|
|
|
|
|
|
Some libraries and applications (like ``mypy`` or ``jinja``) use names
|
|
|
|
|
prepended with a double underscore (i.e., ``__``) as a convention to indicate
|
|
|
|
|
positional-only parameters. We have rejected the idea of introducing ``__`` as
|
|
|
|
|
a new syntax because:
|
|
|
|
|
|
|
|
|
|
* It is a backwards-incompatible change.
|
|
|
|
|
|
|
|
|
|
* It is not symmetric with how the keyword-only parameters are currently
|
|
|
|
|
declared.
|
|
|
|
|
|
2019-04-05 15:25:17 -04:00
|
|
|
|
* Querying the AST for positional-only parameters would require checking the
|
|
|
|
|
normal arguments and inspecting their names, whereas keyword-only parameters
|
2019-04-03 11:31:33 -04:00
|
|
|
|
have a property associated with them (``FunctionDef.args.kwonlyargs``).
|
|
|
|
|
|
|
|
|
|
* Every parameter would need to be inspected to know when positional-only
|
|
|
|
|
arguments end.
|
|
|
|
|
|
|
|
|
|
* The marker is more verbose, forcing marking every positional-only parameter.
|
|
|
|
|
|
|
|
|
|
* It clashes with other uses of the double underscore prefix like invoking name
|
|
|
|
|
mangling in classes.
|
2018-02-01 14:02:37 -05:00
|
|
|
|
|
|
|
|
|
|
2019-04-03 01:08:54 -04:00
|
|
|
|
-------------------------------------------------
|
2019-04-05 18:09:18 -04:00
|
|
|
|
Group Positional-Only Parameters With Parentheses
|
2019-04-03 01:08:54 -04:00
|
|
|
|
-------------------------------------------------
|
|
|
|
|
|
2019-04-03 11:31:33 -04:00
|
|
|
|
Tuple parameter unpacking is a Python 2 feature which allows the use of a tuple
|
|
|
|
|
as a parameter in a function definition. It allows a sequence argument to be
|
|
|
|
|
unpacked automatically. An example is::
|
2019-04-03 01:08:54 -04:00
|
|
|
|
|
|
|
|
|
def fxn(a, (b, c), d):
|
|
|
|
|
pass
|
|
|
|
|
|
2022-01-21 06:03:51 -05:00
|
|
|
|
Tuple argument unpacking was removed in Python 3 (:pep:`3113`). There has been a
|
2019-04-03 01:08:54 -04:00
|
|
|
|
proposition to reuse this syntax to implement positional-only parameters. We
|
|
|
|
|
have rejected this syntax for indicating positional only parameters for several
|
|
|
|
|
reasons:
|
|
|
|
|
|
2019-04-03 11:31:33 -04:00
|
|
|
|
* The syntax is asymmetric with respect to how keyword-only parameters are
|
|
|
|
|
declared.
|
2019-04-03 01:08:54 -04:00
|
|
|
|
|
2019-04-03 11:31:33 -04:00
|
|
|
|
* Python 2 uses this syntax which could raise confusion regarding the behavior
|
|
|
|
|
of this syntax. This would be surprising to users porting Python 2 codebases
|
|
|
|
|
that were using this feature.
|
2019-04-03 01:08:54 -04:00
|
|
|
|
|
|
|
|
|
* This syntax is very similar to tuple literals. This can raise additional
|
|
|
|
|
confusion because it can be confused with a tuple declaration.
|
|
|
|
|
|
2019-04-03 11:31:33 -04:00
|
|
|
|
------------------------
|
2019-04-05 18:09:18 -04:00
|
|
|
|
After Separator Proposal
|
2019-04-03 11:31:33 -04:00
|
|
|
|
------------------------
|
2019-04-05 15:25:17 -04:00
|
|
|
|
|
2019-04-05 18:09:18 -04:00
|
|
|
|
Marking positional-parameters after the ``/`` was another idea considered.
|
2019-04-03 11:31:33 -04:00
|
|
|
|
However, we were unable to find an approach which would modify the arguments
|
|
|
|
|
after the marker. Otherwise, would force the parameters before the marker to
|
|
|
|
|
be positional-only as well. For example::
|
2018-02-01 14:02:37 -05:00
|
|
|
|
|
2019-04-03 11:31:33 -04:00
|
|
|
|
def (x, y, /, z):
|
2019-04-03 01:08:54 -04:00
|
|
|
|
|
2019-04-05 18:09:18 -04:00
|
|
|
|
If we define that ``/`` marks ``z`` as positional-only, it would not be
|
2019-04-03 11:31:33 -04:00
|
|
|
|
possible to specify ``x`` and ``y`` as keyword arguments. Finding a way to
|
|
|
|
|
work around this limitation would add confusion given that at the moment
|
|
|
|
|
keyword arguments cannot be followed by positional arguments. Therefore, ``/``
|
2019-04-05 18:09:18 -04:00
|
|
|
|
would make both the preceding and following parameters positional-only.
|
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
|
2022-01-21 06:03:51 -05:00
|
|
|
|
:pep:`457`.
|
2018-02-01 14:02:37 -05:00
|
|
|
|
|
2019-04-05 18:09:18 -04:00
|
|
|
|
Credit for the use of ``/`` as the separator between positional-only and
|
2019-03-11 19:16:05 -04:00
|
|
|
|
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
|
|
|
|
|
|
|
|
|
|
.. [#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.
|