Currently, Python doesn't allow non-default arguments after default
arguments:
>>> def foo(x=None, y): pass
File "<stdin>", line 1
def foo(x=None, y): pass
^
SyntaxError: non-default argument follows default argument
I believe that at the time this was introduced, no use cases for this
were known and this is is supposed to …
[View More]prevent a source of bugs. I have
two use cases for this, one fringe, but valid, the other more important:
The fringe use case: Suppose you have a function that takes a 2D
coordinate value as separate "x" and "y" arguments. The "x" argument is
optional, the "y" argument isn't. Currently there are two ways to do
this, none of them particularly great:
def foo(y, x): # reverse x and y arguments, confusing
...
def foo(x, y=None): # Treat the x argument as y if only one argument is
provided
if y is None:
x, y = y, x
...
To me, the "natural" solution looks like this:
def foo(x=None, y): ...
# Called like this:
foo(1, 2)
foo(y=2)
This could also be useful when evolving APIs. For example there is a
function "bar" that takes two required arguments. In a later version,
the first argument gains a useful default, the second doesn't. There is
no sensible way to evolve the API at the moment.
The more important use case involves @overloads. A condensed example of
a function where the return type depends on an "encoding" parameter,
followed by further parameters that could be called like this:
foo(123, "utf-8") # would return bytes
foo(encoding="utf-8")
foo(123, None) # would return str
foo(encoding=None)
foo(x=123) # would return str
foo()
This could ideally be written as:
@overload
def foo(x: int = ..., encoding: None = ...) -> str: ...
@overload
def foo(x: int = ..., encoding: str) -> bytes: ...
# plus the actual implementation
But due to the syntax constraint, this needs to be hacked around with a
third overload:
@overload
def foo(x: int = ... encoding: None = ...) -> str: ...
@overload
def foo(x: int, encoding: str) -> bytes: ... # for foo(123, "utf-8")
@overload
def foo(*, encoding: str) -> bytes: ... # for foo(encoding="utf-8")
Not only is this hard to read, real examples in typeshed are usually
more complex, with many arguments before or after the affected argument
or even multiple affected arguments. This often becomes too complex to
write or maintain. Here is one example from the wild:
https://github.com/python/typeshed/blob/b95b729b9e07ab21d252701af0f5b740467…
Allowing non-default arguments after default arguments would solve both
use cases above and eliminates a special case. I'm also not sure what
exactly the current SyntaxError really protects us from. Adding a
non-default after a default argument can't really lead bugs.
- Sebastian
[View Less]
The message was undeliverable due to the following reason(s):
Your message was not delivered because the destination computer was
not reachable within the allowed queue period. The amount of time
a message is queued before it is returned depends on local configura-
tion parameters.
Most likely there is a network problem that prevented delivery, but
it is also possible that the computer is turned off, or does not
have a mail system running right now.
Your message could not be delivered within …
[View More]1 days:
Host 47.86.183.166 is not responding.
The following recipients did not receive this message:
<python-dev(a)python.org>
Please reply to postmaster(a)skippinet.com.au
if you feel this message to be in error.
[View Less]
The original message was received at Wed, 20 Oct 2021 15:12:36 +0300 from acm.org [25.147.223.204]
----- The following addresses had permanent fatal errors -----
python-dev(a)python.org
----- Transcript of the session follows -----
... while talking to 146.160.159.171:
>>> RCPT To:<python-dev(a)python.org>
<<< 550 5.1.1 <python-dev(a)python.org>... Not known here