TL;DR
Unless you explicitly define function parameters as being POSITIONAL_ONLY
or KEYWORD_ONLY
using the syntax below, the default behavior is for all parameters to be POSITIONAL_OR_KEYWORD
. A difference exists between the behavior of Python 3.8+ and previous versions of Python.
Python 3.8+
In Python 3.8+ whether an argument is positional only or keyword only can be specified using the /
and *
syntax, respectively. As an example:
def f(pos1, pos2, /, pos_or_kwd, *, kwd1, kwd2):
----------- ---------- ----------
| | |
| Positional or |
| keyword Keyword only
Positional only
Everything before /
is positional only; everything after *
is keyword only. Note that order matters – /
must come before *
. Also, if you don't explicitly specify POSITIONAL_ONLY
or KEYWORD_ONLY
using this syntax, all arguments default to the POSITIONAL_OR_KEYWORD
value for the kind
attribute.
This is due to a change that was made in Python 3.8. The behavior of the /
syntax was specified in PEP 570 (following PEP 457). In code:
>>> import inspect
# Positional or keyword (default behavior)
>>> def meow (a, b, c = 0, d = 1):
... return (a * b + c) * d
>>> {p.name: p.kind for p in inspect.signature(meow).parameters.values()}
{'a': <_ParameterKind.POSITIONAL_OR_KEYWORD: 1>,
'b': <_ParameterKind.POSITIONAL_OR_KEYWORD: 1>,
'c': <_ParameterKind.POSITIONAL_OR_KEYWORD: 1>,
'd': <_ParameterKind.POSITIONAL_OR_KEYWORD: 1>}
# Positional only, positional or keyword, keyword only
>>> def meow (a, /, b, c = 0, *, d = 1):
... return (a * b + c) * d
>>> {p.name: p.kind for p in inspect.signature(meow).parameters.values()}
{'a': <_ParameterKind.POSITIONAL_ONLY: 1>,
'b': <_ParameterKind.POSITIONAL_OR_KEYWORD: 1>,
'c': <_ParameterKind.POSITIONAL_OR_KEYWORD: 1>,
'd': <_ParameterKind.KEYWORD_ONLY: 1>}
Before Python 3.8
Prior to PEP 570, the /
syntax did not exist but the *
syntax did (haven't been able to find the exact PEP when it was introduced); trying the /
in 3.7 raises a syntax error:
# Python 3.7 - we get an error if use the `/` syntax
>>> def meow (a, /, b, c = 0, *, d = 1):
File "<stdin>", line 1
def meow (a, /, b, c = 0, *, d = 1):
# If we omit the `/` but keep the `*`, it works
>>> def meow (a, b, c = 0, *, d = 1):
... return (a * b + c) * d
>>> {p.name: p.kind for p in inspect.signature(meow).parameters.values()}
{'a': <_ParameterKind.POSITIONAL_OR_KEYWORD: 1>,
'b': <_ParameterKind.POSITIONAL_OR_KEYWORD: 1>,
'c': <_ParameterKind.POSITIONAL_OR_KEYWORD: 1>,
'd': <_ParameterKind.KEYWORD_ONLY: 1>}
Aside from the PEPs, I also found this quick summary helpful to understanding the behavior.