PEP 570: grammar and wording fixes (GH-923)

This commit is contained in:
Pablo Galindo 2019-03-11 23:16:05 +00:00 committed by GitHub
parent bbdcedb71b
commit c0927428bf
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
1 changed files with 85 additions and 81 deletions

View File

@ -3,8 +3,8 @@ Title: Python Positional-Only Parameters
Version: $Revision$ Version: $Revision$
Last-Modified: $Date$ Last-Modified: $Date$
Author: Larry Hastings <larry@hastings.org>, Author: Larry Hastings <larry@hastings.org>,
Pablo Galindo <pablogsal@gmail.com>, Pablo Galindo <pablogsal@gmail.com>,
Mario Corchero <mariocj89@gmail.com> Mario Corchero <mariocj89@gmail.com>
Discussions-To: Python-Dev <python-dev@python.org> Discussions-To: Python-Dev <python-dev@python.org>
Status: Draft Status: Draft
Type: Standards Track Type: Standards Track
@ -33,49 +33,49 @@ positional-only. This changed around Python 1.0, when
all parameters suddenly became positional-or-keyword. all parameters suddenly became positional-or-keyword.
This allowed users to provide arguments to a function both This allowed users to provide arguments to a function both
positionally or referencing the keyword used in the definition positionally or referencing the keyword used in the definition
of it. But, this is not always desired nor even available as of it. But, this is not always desired nor available as
even in current versions of Python, many CPython even in current versions of Python, many CPython
"builtin" functions still only accept positional-only arguments. "builtin" functions still only accept positional-only arguments.
Users might want to restrict their API to not allow for parameters Users might want to restrict their API to not allow for parameters
to be referenced via keyword, as that exposes the name of the to be referenced via keywords, as that exposes the name of the
parameter as part of the API. If a user of said API starts using the parameter as part of the API. If a user of said API starts using the
argument by keyword when calling it and then the name of the parameter argument by keyword when calling it and then the parameter
is changed, it will be a breaking change. By using positional-only gets renamed, it will be a breaking change. By using positional-only
parameters the developer can later change the name of an argument or parameters the developer can later change the name of an arguments or
transform them to ``*args`` without breaking the API. transform them to ``*args`` without breaking the API.
Even if positional arguments only in a function can be achieved Even if making arguments positional-only in a function can be achieved
via using ``*args`` parameters and extracting them one by one, by using ``*args`` parameters and extracting them one by one,
the solution is far from ideal and not as expressive as the one the solution is far from ideal and not as expressive as the one
proposed in this PEP, which targets to provide syntax to specify proposed in this PEP, which targets providing syntax to specify
accepting a specific number of positional-only parameters. Also, accepting a specific number of positional-only parameters. Also,
it makes the signature of the function ambiguous as users won't it makes the signature of the function ambiguous as users won't
know how many parameters the function takes by looking at help() know how many parameters the function takes by looking at ``help()``
or auto-generated documentation. or auto-generated documentation.
Additionally, this will bridge the gap we currently find between Additionally, this will bridge the gap we currently find between
builtin functions that today allows to specify positional-only builtin functions that can specify positional-only
parameters and pure Python implementations that lack the parameters and pure Python implementations that lack the
syntax for it. The '/' syntax is already exposed in the syntax for it. The '/' syntax is already exposed in the
documentation for some builtins and interfaces generated by documentation of some builtins and interfaces generated by
the argument clinic. Making positional only arguments a possibility the argument clinic. Making positional-only arguments a possibility
in Python will bring consistency and will remove confusion from in Python will make the language more consistent and make it clearer
users that are not familiarized with the fact that positional only to users that positional-only arguments are allowed in builtins and argument
arguments are allowed in builtins and argument clinic C interfaces. clinic C interfaces.
We find this useful in multiple situation, say for example we We can find positional-only arguments useful in several situations. Example:
are looking at creating a function that converts from one type to we want to create a function that converts from one type to
another:: another::
def as_my_type(x): def as_my_type(x):
... ...
The name of the parameter provides no value whatsoever and forces The name of the parameter provides no value whatsoever, and forces
the user to maintain it's name forever as user might rely on it the developer to maintain its name forever, as users might pass ``x`` as a
being used as a keyword only. keyword.
Another good example is APIs that want to transmit the feeling Another good example is an API that wants to transmit the feeling
of ownership through positional arguments, see:: of ownership through positional arguments, see::
class MyDecorator: class MyDecorator:
@ -83,20 +83,20 @@ of ownership through positional arguments, see::
... ...
Again we get no value from using keyword arguments here and it can limit Again we get no value from using keyword arguments here and it can limit
future evolutions of the API. Say at a later time we want the decorator future evolution of the API. Say at a later time we want the decorator
to be able to take multiple functions, we will be forced to keep to be able to take multiple functions, we will be forced to always keep
the original argument always or we'd potentially break users. the original argument or we would potentially break users.
By being able to define positional only arguments we can change the By being able to define positional-only arguments we can change the
name of those at will or even change them by ``*args``. name of those at will or even change them to ``*args``.
----------------------------------------------------- ---------------------------------------------------
Positional-Only Parameter Semantics In Current Python Positional-Only Parameter Semantics In Python Today
----------------------------------------------------- ---------------------------------------------------
There are many, many examples of builtins that only There are many, many examples of builtins that only
accept positional-only parameters. The resulting accept positional-only parameters. The resulting
semantics are easily experienced by the Python semantics are easily experienced by the Python
programmer--just try calling one, specifying its programmer -- just try calling one, specifying its
arguments by name:: arguments by name::
@ -109,8 +109,8 @@ arguments by name::
File "<stdin>", line 1, in <module> File "<stdin>", line 1, in <module>
TypeError: pow() takes no keyword arguments TypeError: pow() takes no keyword arguments
Pow clearly expresses that its arguments are only positional ``pow()`` clearly expresses that its arguments are only positional
via the ``/`` marker, but this is at the moment only documentational, via the ``/`` marker, but this at the moment is only documentational,
Python developers cannot write such syntax. Python developers cannot write such syntax.
In addition, there are some functions with particularly In addition, there are some functions with particularly
@ -128,9 +128,9 @@ interesting semantics:
Obviously one can simulate any of these in pure Python code Obviously one can simulate any of these in pure Python code
by accepting ``(*args, **kwargs)`` and parsing the arguments by accepting ``(*args, **kwargs)`` and parsing the arguments
by hand. But this results in a disconnect between the by hand. But this results in a disconnect between the
Python function signature and what it actually accepts, Python function signature and what the function actually accepts,
not to mention the work of implementing said argument parsing not to mention the work of implementing said argument parsing
and the lack of clarity that generates in the signature. and the lack of clarity in the resulting signature.
========== ==========
Motivation Motivation
@ -138,28 +138,28 @@ Motivation
The new syntax will allow developers to further control how their The new syntax will allow developers to further control how their
API can be consumed. It will allow restricting the usage of keyword API can be consumed. It will allow restricting the usage of keyword
Specify arguments by adding the new type of positional-only ones. arguments by adding the new type of positional-only ones.
A similar PEP with a broader scope (PEP 457) was proposed A similar PEP with a broader scope (PEP 457) was proposed earlier
to define the syntax. This PEP builds on top of part of it to define the syntax. This PEP builds partially on top of that,
to define and provide an implementation for the ``/`` syntax on to define and provide an implementation for the ``/`` syntax in
function signatures. function signatures.
Providing positional only arguments will allow for maintaining the Providing positional-only arguments will allow for maintaining the
interface when creating pure Python implementation of C modules, which interface when creating pure Python implementation of C modules, which
provides not only the API benefits outlined in this document but it is provides not only the API benefits outlined in this document but it is
also faster, see this thread about converting keyword arguments to positional also faster. See this thread about converting keyword arguments to positional:
[#thread-keyword-to-positional]_ and PEP-399 [#PEP399]_, which requires the [#thread-keyword-to-positional]_ and PEP-399 [#PEP399]_, which requires the
same API for C accelerators than the Python implementation. same API for C accelerators as the Python implementation.
There has been multiple changes in builtin functions that moved away There have been multiple changes in builtin functions that moved away
from keyword arguments, like ``bool``, ``float``, ``list``, ``int``, ``tuple`` from keyword arguments, like ``bool``, ``float``, ``list``, ``int``, ``tuple``
which is a non-backward compatible. By having proper support for which is a non-backward compatible change. By having proper support for
positional only arguments, this kind of APIs where it is clear that positional-only arguments, these kind of APIs, where it is clear that
passing a keyword argument provides no clarity would be able to passing a keyword argument provides no clarity, it would be possible to
follow a similar approach that those builtins without breaking users. follow a similar approach as these builtins, without breaking users.
This is a well discussed recurring topic in the Python mailing lists: This is a well discussed, recurring topic on the Python mailing lists:
* September 2018: `Anders Hovmöller: [Python-ideas] Positional-only * September 2018: `Anders Hovmöller: [Python-ideas] Positional-only
parameters parameters
@ -193,10 +193,10 @@ and inconsistent:
* Some functions use *both* of the above approaches. [#RANGE]_ [#ADDCH]_ * Some functions use *both* of the above approaches. [#RANGE]_ [#ADDCH]_
One more important idea to consider: currently in the documentation One more important idea to consider: currently in the documentation
there's no way to tell whether a function takes positional-only there is no way to tell whether a function takes positional-only
parameters. ``open()`` accepts keyword arguments, ``ord()`` does parameters. ``open()`` accepts keyword arguments, ``ord()`` does
not, but there is no way of telling just by reading the not, but there is no way of telling just by reading the
documentation that this is true. documentation.
==================== ====================
Syntax And Semantics Syntax And Semantics
@ -217,10 +217,10 @@ All parameters before the ``/`` are positional-only. If ``/`` is
not specified in a function signature, that function does not not specified in a function signature, that function does not
accept any positional-only parameters. accept any positional-only parameters.
The logic around optional values for positional-only argument The logic around optional values for positional-only argument
Remains the same as the one for positional-or-keyword. Once remains the same as the one for positional-or-keyword. Once
a positional-only argument is provided with a default, a positional-only argument is provided with a default,
the following positional-only and positional-or-keyword argument the following positional-only and positional-or-keyword argument
need to have a default as well. Positional-only parameters that needs to have a default as well. Positional-only parameters that
dont have a default value are "required" positional-only parameters. dont have a default value are "required" positional-only parameters.
Therefore the following are valid signatures:: Therefore the following are valid signatures::
@ -249,11 +249,13 @@ A draft of the proposed grammar specification is::
new_varargslist: new_varargslist:
vfpdef ['=' test] (',' vfpdef ['=' test])* ',' '/' [',' [varargslist]] | varargslist vfpdef ['=' test] (',' vfpdef ['=' test])* ',' '/' [',' [varargslist]] | varargslist
It will be added to the actual typedargslist and varargslist but for easier discussion is It will be added to the actual typedargslist and varargslist but for easier
presented as new_typedargslist and new_varargslist. Also, notice that using a construction discussion it is presented as new_typedargslist and new_varargslist. Also,
using two new rules (new_varargslist and new_varargslist) is not possible with the current notice that using a construction with two new rules (new_varargslist and
parser as the rule is not LL(1). This is the reason the rule needs to be include in the new_varargslist) is not possible with the current parser as the rule is not
existing typedargslist and varargslist (in the same way keyword-only arguments were introduced). 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).
============== ==============
@ -285,21 +287,21 @@ After marker proposal
--------------------- ---------------------
A complaint against the proposal is the fact that the modifier of A complaint against the proposal is the fact that the modifier of
the signature impacts the "already passed" tokens. the signature impacts the tokens already passed.
This might make confusing to "human parsers" to read functions This might make it confusing to users to read functions
with many arguments. Example:: with many arguments. Example::
def really_bad_example_of_a_python_function(fist_long_argument, second_long_argument, def really_bad_example_of_a_python_function(fist_long_argument, second_long_argument,
third_long_argument, /): third_long_argument, /):
It is not until you reach the end of the signature that the reader It is not until reaching the end of the signature that the reader
realized the ``/`` and therefore the fact that the arguments are realizes the ``/``, and therefore the fact that the arguments are
position-only. This deviates from how the keyword-only marker works. position-only. This deviates from how the keyword-only marker works.
That said we could not find an implementation that would modify the That said we could not find an implementation that would modify the
arguments after the marker, as that will force the one before the arguments after the marker, as that will force the one before the
marker to be position only as well. Example:: marker to be position-only as well. Example::
def (x, y, /, z): def (x, y, /, z):
@ -307,48 +309,50 @@ If we define that ``/`` makes only z position-only it won't be possible
to call x and y via keyword argument. Finding a way to work around it 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 will add confusion given that at the moment keyword arguments cannot be
followed by positional arguments. ``/`` will therefore make both the followed by positional arguments. ``/`` will therefore make both the
preceding and following position-only. preceding and following parameters position-only.
------------------- -------------------
Per-argument marker Per-argument marker
------------------- -------------------
Using a per argument marker might be an option as well. The approach Using a per-argument marker might be an option as well. The approach
basically adds a token to each of the arguments that are position only basically adds a token to each of the arguments that are position only
and requires those to be placed together. Example:: and requires those to be placed together. Example::
def (.arg1, .arg2, arg3): def (.arg1, .arg2, arg3):
Note the dot on arg1 and arg2. Even if this approach might look easier Note the dot on arg1 and arg2. Even if this approach might look easier
to read it has been discarded as ``/`` goes further inline with the to read, it has been discarded as ``/`` goes further in line with the
keyword-only approach and is less error prone. keyword-only approach and is less error-prone.
There are some libraries that use leading underscore[#leading-underscore]_ There are some libraries that use leading underscore[#leading-underscore]_
to mark those arguments as positional only. to mark those arguments as positional-only.
---------------- ----------------
Using decorators Using decorators
---------------- ----------------
It has been suggested on python-ideas [#python-ideas-decorator-based]_ to provide It has been suggested on python-ideas [#python-ideas-decorator-based]_ to
a decorator written in Python as an implementation for this feature. This approach provide a decorator written in Python as an implementation for this feature.
has the advantage that keeps parameter declaration more easy to read but also This approach has the advantage that keeps parameter declaration more easy to
introduces an asymmetry on how parameter behaviour is declared. Also, as the ``/`` read but also introduces an asymmetry on how parameter behaviour is declared.
syntax is already introduced for C functions, this inconsistency will make more Also, as the ``/`` syntax is already introduced for C functions, this
difficult to implement all tools and modules that deal with this syntax including inconsistency will make it more difficult to implement all tools and modules
but not limited to, the argument clinic, the inspect module and the ast module. that deal with this syntax including but not limited to, the argument clinic,
Another disadvantage of this approach is that calling the decorated functions the inspect module and the ast module. Another disadvantage of this approach
will be slower than the functions generated if the feature was implemented directly is that calling the decorated functions will be slower than the functions
in C. generated if the feature was implemented directly in C.
====== ======
Thanks Thanks
====== ======
Credit for most of the content of this PEP is contained in Larry Hastingss PEP 457. Credit for most of the content of this PEP is contained in Larry Hastingss
PEP 457.
Credit for the use of '/' as the separator between positional-only and positional-or-keyword Credit for the use of '/' as the separator between positional-only and
parameters go to Guido van Rossum, in a proposal from 2012. [#GUIDO]_ positional-or-keyword parameters go to Guido van Rossum, in a proposal from
2012. [#GUIDO]_
Credit for discussion about the simplification of the grammar goes to Credit for discussion about the simplification of the grammar goes to
Braulio Valdivieso. Braulio Valdivieso.