Skip to content

Commit b76302d

Browse files
pablogsalwillingc
authored andcommitted
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 <[email protected]> * Update Doc/tutorial/controlflow.rst Co-Authored-By: Carol Willing <[email protected]> * Add extra bullet point and minor edits
1 parent 77f0ed7 commit b76302d

File tree

5 files changed

+182
-10
lines changed

5 files changed

+182
-10
lines changed

Doc/c-api/code.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ bound into a function.
3333
3434
Return the number of free variables in *co*.
3535
36-
.. 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)
36+
.. 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)
3737
3838
Return a new code object. If you need a dummy code object to
3939
create a frame, use :c:func:`PyCode_NewEmpty` instead. Calling

Doc/data/refcounts.dat

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -236,6 +236,7 @@ PyCode_GetNumFree:PyCodeObject*:co:0:
236236

237237
PyCode_New:PyCodeObject*::+1:
238238
PyCode_New:int:argcount::
239+
PyCode_New:int:posonlyargcount::
239240
PyCode_New:int:kwonlyargcount::
240241
PyCode_New:int:nlocals::
241242
PyCode_New:int:stacksize::

Doc/library/inspect.rst

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -169,6 +169,9 @@ attributes:
169169
| | | variables (referenced via |
170170
| | | a function's closure) |
171171
+-----------+-------------------+---------------------------+
172+
| | co_posonlyargcount| number of positional only |
173+
| | | arguments |
174+
+-----------+-------------------+---------------------------+
172175
| | co_kwonlyargcount | number of keyword only |
173176
| | | arguments (not including |
174177
| | | \*\* arg) |
@@ -724,13 +727,9 @@ function.
724727
| Name | Meaning |
725728
+========================+==============================================+
726729
| *POSITIONAL_ONLY* | Value must be supplied as a positional |
727-
| | argument. |
728-
| | |
729-
| | Python has no explicit syntax for defining |
730-
| | positional-only parameters, but many built-in|
731-
| | and extension module functions (especially |
732-
| | those that accept only one or two parameters)|
733-
| | accept them. |
730+
| | argument. Positional only parameters are |
731+
| | those which appear before a ``/`` entry (if |
732+
| | present) in a Python function definition. |
734733
+------------------------+----------------------------------------------+
735734
| *POSITIONAL_OR_KEYWORD*| Value may be supplied as either a keyword or |
736735
| | positional argument (this is the standard |

Doc/reference/compound_stmts.rst

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -483,8 +483,10 @@ A function definition defines a user-defined function object (see section
483483
decorators: `decorator`+
484484
decorator: "@" `dotted_name` ["(" [`argument_list` [","]] ")"] NEWLINE
485485
dotted_name: `identifier` ("." `identifier`)*
486-
parameter_list: `defparameter` ("," `defparameter`)* ["," [`parameter_list_starargs`]]
487-
: | `parameter_list_starargs`
486+
parameter_list: `defparameter` ("," `defparameter`)* ',' '/' [',' [`parameter_list_no_posonly`]]
487+
: | `parameter_list_no_posonly`
488+
parameter_list_no_posonly: `defparameter` ("," `defparameter`)* ["," [`parameter_list_starargs`]]
489+
: | `parameter_list_starargs`
488490
parameter_list_starargs: "*" [`parameter`] ("," `defparameter`)* ["," ["**" `parameter` [","]]]
489491
: | "**" `parameter` [","]
490492
parameter: `identifier` [":" `expression`]

Doc/tutorial/controlflow.rst

Lines changed: 170 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -519,6 +519,176 @@ and of course it would print:
519519
Note that the order in which the keyword arguments are printed is guaranteed
520520
to match the order in which they were provided in the function call.
521521

522+
Special parameters
523+
------------------
524+
525+
By default, arguments may be passed to a Python function either by position
526+
or explicitly by keyword. For readability and performance, it makes sense to
527+
restrict the way arguments can be passed so that a developer need only look
528+
at the function definition to determine if items are passed by position, by
529+
position or keyword, or by keyword.
530+
531+
A function definition may look like:
532+
533+
.. code-block:: none
534+
535+
def f(pos1, pos2, /, pos_or_kwd, *, kwd1, kwd2):
536+
----------- ---------- ----------
537+
| | |
538+
| Positional or keyword |
539+
| - Keyword only
540+
-- Positional only
541+
542+
where ``/`` and ``*`` are optional. If used, these symbols indicate the kind of
543+
parameter by how the arguments may be passed to the function:
544+
positional-only, positional-or-keyword, and keyword-only. Keyword parameters
545+
are also referred to as named parameters.
546+
547+
-------------------------------
548+
Positional-or-Keyword Arguments
549+
-------------------------------
550+
551+
If ``/`` and ``*`` are not present in the function definition, arguments may
552+
be passed to a function by position or by keyword.
553+
554+
--------------------------
555+
Positional-Only Parameters
556+
--------------------------
557+
558+
Looking at this in a bit more detail, it is possible to mark certain parameters
559+
as *positional-only*. If *positional-only*, the parameters' order matters, and
560+
the parameters cannot be passed by keyword. Positional-only parameters are
561+
placed before a ``/`` (forward-slash). The ``/`` is used to logically
562+
separate the positional-only parameters from the rest of the parameters.
563+
If there is no ``/`` in the function definition, there are no positional-only
564+
parameters.
565+
566+
Parameters following the ``/`` may be *positional-or-keyword* or *keyword-only*.
567+
568+
----------------------
569+
Keyword-Only Arguments
570+
----------------------
571+
572+
To mark parameters as *keyword-only*, indicating the parameters must be passed
573+
by keyword argument, place an ``*`` in the arguments list just before the first
574+
*keyword-only* parameter.
575+
576+
-----------------
577+
Function Examples
578+
-----------------
579+
580+
Consider the following example function definitions paying close attention to the
581+
markers ``/`` and ``*``::
582+
583+
>>> def standard_arg(arg):
584+
... print(arg)
585+
...
586+
>>> def pos_only_arg(arg, /):
587+
... print(arg)
588+
...
589+
>>> def kwd_only_arg(*, arg):
590+
... print(arg)
591+
...
592+
>>> def combined_example(pos_only, /, standard, *, kwd_only):
593+
... print(pos_only, standard, kwd_only)
594+
595+
596+
The first function definition, ``standard_arg``, the most familiar form,
597+
places no restrictions on the calling convention and arguments may be
598+
passed by position or keyword::
599+
600+
>>> standard_arg(2)
601+
2
602+
603+
>>> standard_arg(arg=2)
604+
2
605+
606+
The second function ``pos_only_arg`` is restricted to only use positional
607+
parameters as there is a ``/`` in the function definition::
608+
609+
>>> pos_only_arg(1)
610+
1
611+
612+
>>> pos_only_arg(arg=1)
613+
Traceback (most recent call last):
614+
File "<stdin>", line 1, in <module>
615+
TypeError: pos_only_arg() got an unexpected keyword argument 'arg'
616+
617+
The third function ``kwd_only_args`` only allows keyword arguments as indicated
618+
by a ``*`` in the function definition::
619+
620+
>>> kwd_only_arg(3)
621+
Traceback (most recent call last):
622+
File "<stdin>", line 1, in <module>
623+
TypeError: kwd_only_arg() takes 0 positional arguments but 1 was given
624+
625+
>>> kwd_only_arg(arg=3)
626+
3
627+
628+
And the last uses all three calling conventions in the same function
629+
definition::
630+
631+
>>> combined_example(1, 2, 3)
632+
Traceback (most recent call last):
633+
File "<stdin>", line 1, in <module>
634+
TypeError: combined_example() takes 2 positional arguments but 3 were given
635+
636+
>>> combined_example(1, 2, kwd_only=3)
637+
1 2 3
638+
639+
>>> combined_example(1, standard=2, kwd_only=3)
640+
1 2 3
641+
642+
>>> combined_example(pos_only=1, standard=2, kwd_only=3)
643+
Traceback (most recent call last):
644+
File "<stdin>", line 1, in <module>
645+
TypeError: combined_example() got an unexpected keyword argument 'pos_only'
646+
647+
648+
Finally, consider this function definition which has a potential collision between the positional argument ``name`` and ``**kwds`` which has ``name`` as a key::
649+
650+
def foo(name, **kwds):
651+
return 'name' in kwds
652+
653+
There is no possible call that will make it return ``True`` as the keyword ``'name'``
654+
will always to bind to the first parameter. For example::
655+
656+
>>> foo(1, **{'name': 2})
657+
Traceback (most recent call last):
658+
File "<stdin>", line 1, in <module>
659+
TypeError: foo() got multiple values for argument 'name'
660+
>>>
661+
662+
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::
663+
664+
def foo(name, /, **kwds):
665+
return 'name' in kwds
666+
>>> foo(1, **{'name': 2})
667+
True
668+
669+
In other words, the names of positional-only parameters can be used in
670+
``**kwds`` without ambiguity.
671+
672+
-----
673+
Recap
674+
-----
675+
676+
The use case will determine which parameters to use in the function definition::
677+
678+
def f(pos1, pos2, /, pos_or_kwd, *, kwd1, kwd2):
679+
680+
As guidance:
681+
682+
* Use positional-only if you want the name of the parameters to not be
683+
available to the user. This is useful when parameter names have no real
684+
meaning, if you want to enforce the order of the arguments when the function
685+
is called or if you need to take some positional parameters and arbitrary
686+
keywords.
687+
* Use keyword-only when names have meaning and the function definition is
688+
more understandable by being explicit with names or you want to prevent
689+
users relying on the position of the argument being passed.
690+
* For an API, use positional-only to prevent prevent breaking API changes
691+
if the parameter's name is modified in the future.
522692

523693
.. _tut-arbitraryargs:
524694

0 commit comments

Comments
 (0)