Consider the following function (signature):
def f(p1, p2, /, pk1, pk2, *, k1, k2): pass
This has two positional-only, two keyword-only, and two positional-or-keyword args.
If p2,
is removed then it’s still valid, and now has only one positional-only arg.
If p1,
is removed as well then it’s suddenly a SyntaxError
:
>>> def f(p1, p2, /, pk1, pk2, *, k1, k2): pass
...
>>> def f(p1, /, pk1, pk2, *, k1, k2): pass
...
>>> def f(/, pk1, pk2, *, k1, k2): pass
File "<stdin>", line 1
def f(/, pk1, pk2, *, k1, k2): pass
^
SyntaxError: at least one argument must precede /
Similarly, if , k2
is removed then the signature is still valid and has only one keyword-only arg.
If , k1
is removed as well then it’s a SyntaxError
:
>>> def f(p1, p2, /, pk1, pk2, *, k1, k2): pass
...
>>> def f(p1, p2, /, pk1, pk2, *, k1): pass
...
>>> def f(p1, p2, /, pk1, pk2, *): pass
File "<stdin>", line 1
def f(p1, p2, /, pk1, pk2, *): pass
^
SyntaxError: named arguments must follow bare *
I feel that /
at the start of args and *
at the end of args should be allowed, for two reasons:
- Compare
def f(a, b)
withdef f(a, /, b)
. Clearly, the latter indicates that the author has thought about which args should be positional-only, and has determined thata
should be positional-only andb
should not. In the former case, maybe the author’s intent was for botha
andb
to be positional-or-keyword, but maybe they hadn’t thought about whether they should be allowed to be passed as keyword-arguments at all. Now, comparedef f(b)
withdef f(/, b)
. The latter case is currently aSyntaxError
, but if allowed, it would indicate with certainty the author’s intent thatb
be allowed to be passed as a keyword-argument. - During refactoring, if I have functions that have some positional-only args, and add/remove some/all of them at various points in the refactor (not knowing how the function will ultimately end up), if at any point all the positional-only args disappear, I either have to remove the
/
so that it’s not an error (which can lead to me forgetting to add it back later), or put up with there being an error until I’m finished, and only then remove any stray/
's.
The above are written regarding /
, but equivalent logic applies to *
when swapping “positional” with “keyword” and reversing the order of the arguments.
I imagine this should be a simple enough change to make in “next 3.x”.
If an author prefers the current behavior for code style reasons, that could be expressed as a linter rule. Similarly, with the new behavior, one could even imagine cases where an author wants to require the “positional-or-keyword-ness” of every argument to always be explicitly specified, which means a linter rule to require both /
and *
in every function signature.
Are there any downsides to this that I’m not noticing?