summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorPablo Galindo <Pablogsal@gmail.com>2019-05-28 23:45:32 (GMT)
committerCarol Willing <carolcode@willingconsulting.com>2019-05-28 23:45:32 (GMT)
commitb76302ddd0896cb39ce69909349b53db6e7776e2 (patch)
tree5f9a80489decdb07f40e97cf618d62f0ce5b4930
parent77f0ed7a42606d03ebfe48ab152caf0d796d6540 (diff)
downloadcpython-b76302ddd0896cb39ce69909349b53db6e7776e2.zip
cpython-b76302ddd0896cb39ce69909349b53db6e7776e2.tar.gz
cpython-b76302ddd0896cb39ce69909349b53db6e7776e2.tar.bz2
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
-rw-r--r--Doc/c-api/code.rst2
-rw-r--r--Doc/data/refcounts.dat1
-rw-r--r--Doc/library/inspect.rst13
-rw-r--r--Doc/reference/compound_stmts.rst6
-rw-r--r--Doc/tutorial/controlflow.rst170
5 files changed, 182 insertions, 10 deletions
diff --git a/Doc/c-api/code.rst b/Doc/c-api/code.rst
index fd3f691..e2b0b23 100644
--- a/Doc/c-api/code.rst
+++ b/Doc/c-api/code.rst
@@ -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
diff --git a/Doc/data/refcounts.dat b/Doc/data/refcounts.dat
index 213ddcb..aca57a1 100644
--- a/Doc/data/refcounts.dat
+++ b/Doc/data/refcounts.dat
@@ -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::
diff --git a/Doc/library/inspect.rst b/Doc/library/inspect.rst
index 81824dd..1cc503a 100644
--- a/Doc/library/inspect.rst
+++ b/Doc/library/inspect.rst
@@ -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 |
diff --git a/Doc/reference/compound_stmts.rst b/Doc/reference/compound_stmts.rst
index 42fa864..bf53cb5 100644
--- a/Doc/reference/compound_stmts.rst
+++ b/Doc/reference/compound_stmts.rst
@@ -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`]
diff --git a/Doc/tutorial/controlflow.rst b/Doc/tutorial/controlflow.rst
index cfb9645..813b7ca 100644
--- a/Doc/tutorial/controlflow.rst
+++ b/Doc/tutorial/controlflow.rst
@@ -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: