bpo-36540: Documentation for PEP570 - Python positional only arguments (#13202)

* bpo-36540: Documentation for PEP570 - Python positional only arguments

* fixup! bpo-36540: Documentation for PEP570 - Python positional only arguments

* Update reference for compound statements

* Apply suggestions from Carol

Co-Authored-By: Carol Willing <carolcode@willingconsulting.com>

* Update Doc/tutorial/controlflow.rst

Co-Authored-By: Carol Willing <carolcode@willingconsulting.com>

* Add extra bullet point and minor edits
This commit is contained in:
Pablo Galindo 2019-05-29 00:45:32 +01:00 committed by Carol Willing
parent 77f0ed7a42
commit b76302ddd0
5 changed files with 182 additions and 10 deletions

View File

@ -33,7 +33,7 @@ bound into a function.
Return the number of free variables in *co*.
.. c:function:: PyCodeObject* PyCode_New(int argcount, int kwonlyargcount, int nlocals, int stacksize, int flags, PyObject *code, PyObject *consts, PyObject *names, PyObject *varnames, PyObject *freevars, PyObject *cellvars, PyObject *filename, PyObject *name, int firstlineno, PyObject *lnotab)
.. c:function:: PyCodeObject* PyCode_New(int argcount, int posonlyargcount, int kwonlyargcount, int nlocals, int stacksize, int flags, PyObject *code, PyObject *consts, PyObject *names, PyObject *varnames, PyObject *freevars, PyObject *cellvars, PyObject *filename, PyObject *name, int firstlineno, PyObject *lnotab)
Return a new code object. If you need a dummy code object to
create a frame, use :c:func:`PyCode_NewEmpty` instead. Calling

View File

@ -236,6 +236,7 @@ PyCode_GetNumFree:PyCodeObject*:co:0:
PyCode_New:PyCodeObject*::+1:
PyCode_New:int:argcount::
PyCode_New:int:posonlyargcount::
PyCode_New:int:kwonlyargcount::
PyCode_New:int:nlocals::
PyCode_New:int:stacksize::

View File

@ -169,6 +169,9 @@ attributes:
| | | variables (referenced via |
| | | a function's closure) |
+-----------+-------------------+---------------------------+
| | co_posonlyargcount| number of positional only |
| | | arguments |
+-----------+-------------------+---------------------------+
| | co_kwonlyargcount | number of keyword only |
| | | arguments (not including |
| | | \*\* arg) |
@ -724,13 +727,9 @@ function.
| Name | Meaning |
+========================+==============================================+
| *POSITIONAL_ONLY* | Value must be supplied as a positional |
| | argument. |
| | |
| | Python has no explicit syntax for defining |
| | positional-only parameters, but many built-in|
| | and extension module functions (especially |
| | those that accept only one or two parameters)|
| | accept them. |
| | argument. Positional only parameters are |
| | those which appear before a ``/`` entry (if |
| | present) in a Python function definition. |
+------------------------+----------------------------------------------+
| *POSITIONAL_OR_KEYWORD*| Value may be supplied as either a keyword or |
| | positional argument (this is the standard |

View File

@ -483,8 +483,10 @@ A function definition defines a user-defined function object (see section
decorators: `decorator`+
decorator: "@" `dotted_name` ["(" [`argument_list` [","]] ")"] NEWLINE
dotted_name: `identifier` ("." `identifier`)*
parameter_list: `defparameter` ("," `defparameter`)* ["," [`parameter_list_starargs`]]
: | `parameter_list_starargs`
parameter_list: `defparameter` ("," `defparameter`)* ',' '/' [',' [`parameter_list_no_posonly`]]
: | `parameter_list_no_posonly`
parameter_list_no_posonly: `defparameter` ("," `defparameter`)* ["," [`parameter_list_starargs`]]
: | `parameter_list_starargs`
parameter_list_starargs: "*" [`parameter`] ("," `defparameter`)* ["," ["**" `parameter` [","]]]
: | "**" `parameter` [","]
parameter: `identifier` [":" `expression`]

View File

@ -519,6 +519,176 @@ and of course it would print:
Note that the order in which the keyword arguments are printed is guaranteed
to match the order in which they were provided in the function call.
Special parameters
------------------
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.
A function definition may look like:
.. code-block:: none
def f(pos1, pos2, /, pos_or_kwd, *, kwd1, kwd2):
----------- ---------- ----------
| | |
| Positional or keyword |
| - Keyword only
-- Positional only
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.
--------------------------
Positional-Only Parameters
--------------------------
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 are
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*.
----------------------
Keyword-Only Arguments
----------------------
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 ``*``::
>>> 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)
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::
>>> standard_arg(2)
2
>>> standard_arg(arg=2)
2
The second function ``pos_only_arg`` is restricted to only use positional
parameters as there is a ``/`` in the function definition::
>>> pos_only_arg(1)
1
>>> 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'
The third function ``kwd_only_args`` only allows keyword arguments as indicated
by a ``*`` in the function definition::
>>> 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
>>> kwd_only_arg(arg=3)
3
And the last uses all three calling conventions in the same function
definition::
>>> 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
>>> combined_example(1, 2, kwd_only=3)
1 2 3
>>> combined_example(1, standard=2, kwd_only=3)
1 2 3
>>> 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'
Finally, consider this function definition which has a potential collision between the positional argument ``name`` and ``**kwds`` which has ``name`` as a key::
def foo(name, **kwds):
return 'name' in kwds
There is no possible call that will make it return ``True`` as the keyword ``'name'``
will always to bind to the first parameter. 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 ``/`` (positional only arguments), it is possible since it allows ``name`` as a positional argument and ``'name'`` as a key in the keyword arguments::
def foo(name, /, **kwds):
return 'name' in kwds
>>> foo(1, **{'name': 2})
True
In other words, the names of positional-only parameters can be used in
``**kwds`` without ambiguity.
-----
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 you want the name of the parameters to not be
available to the user. This is useful when parameter names have no real
meaning, if you want to enforce the order of the arguments when the function
is called or if you need to take some positional parameters and arbitrary
keywords.
* Use keyword-only when names have meaning and the function definition is
more understandable by being explicit with names or you want to prevent
users relying on the position of the argument being passed.
* For an API, use positional-only to prevent prevent breaking API changes
if the parameter's name is modified in the future.
.. _tut-arbitraryargs: