def foo(*,a,b,c)...
or def foo(obvious_and_required_thing, *, a,b,c) ...
and noticed an immediate improvement in clarity. def foo(*,a,b,c)...
or def foo(obvious_and_required_thing, *, a,b,c) ...
and noticed an immediate improvement in clarity.I think JavaScript got this right where the semantic resolution steps of arguments pretty closely follow the steps for assignment, which means once someone understands the basic idea of object destructuring they get named arguments for free, in the form of destructured object parameters:
const foo = (positionalArg1, {namedArg1, namedArg2 = 'foo'}) => { ... }
foo(1, {namedArg: 'bar'})
// resolved same as
const [positionalArg1, {namedArg, namedArg2 = 'foo'}] = [1, {namedArg: 'bar'}]I think it's much more beautiful and convenient than your JS example which in all honesty looks necessary cluttered and ugly.
> cluttered and ugly
I won’t argue with you about visuals because I don’t think they’re all that important. But, I think it’s undeniable that reusing the same syntactic concepts across different semantic use cases is quite a lot more logically clean & beautiful than introducing new syntax that other HN commenters with past python experience can’t even understand. If someone sees the JS named parameter syntax at a declaration and has any JS experience, they’ll almost certainly figure out what to do - if someone sees the asterisk, even with Python experience, they’ll think “what the heck is this asterisk?” And again, the other commenters on this thread are clear evidence that this is indeed as unintuitive as I describe.
There's a collection of 0 arguments. the "∗" was introduced specifically as a shortcut for:
def foo(a, *ignore, b):
if ignore:
raise TypeError
forgoing the name simply signals to the language that it should collect nothing.It's a logical extension of the assumption that anything which follows `∗arg` is a keyword-only parameter.
*args
creates a catch-all variadic function parameter called `args` which matches all positional parameters not heretofore assigned *
(same operator, without a name following) indicates that no more positional parameters may be matched after this pointThey are close enough cognitively to warrant reuse IMO.
Huh? It's both making parameters more explicit and functions using it more readable and self-documenting.
In fact, the old way, before bare / and * in args list, where functions essentially had mandatory optional-keyword arguments made every function create an API in violation of “There should be one-- and preferably only one --obvious way to do it.”
It is very much not readable, as evidenced by the other python-experienced commenters on this thread having no clue what it means (“readability” doesn’t mean “easy to understand for people who already know what it means”, as achieving that is a very low bar. What should be targeted is “obvious enough to have a clear meaning even to those who haven’t studied the exact section of the spec”, which is possible in the JS approach as they’re reusing the same syntactic constructs throughout the language, but is impossible in the Python approach because they introduce new syntax to work around every little limitation of their existing syntax)
Named argument are more explicit.
I wrote a lot of old python and js code with dict/object as argument, they were dreadful
For python-level functions initially all arguments could be passed either positionally or by keyword, unless they slotted into the special `∗args` (positional-only) or `∗∗kwargs` (keyword-only) parameters (note: the star-prefixes is the important part here the names can be anything).
Now I'm saying this was for python-level functions because (AFAIK) at the C level it was always possible to rather easily define positional-only or keyword-only arguments.
Anyway one of the improvements of Python 3 was to allow parameters other than `∗∗kwargs` after `∗args`, with the effect of making them individually named but keyword-only (PEP 3102). A logical extension of the same was to allow keyword-only arguments without any positional, that's what the `*` pseudo-argument defines:
> The reasoning behind this change is as follows. Imagine for a moment a function which takes several positional arguments, as well as a keyword argument.
> Now, suppose you wanted to have 'key' be a keyword-only argument. Under the above syntax, you could accomplish this by adding a varargs (nb: `∗ignore`) argument immediately before the keyword argument.
> Unfortunately, the 'ignore' argument will also suck up any erroneous positional arguments that may have been supplied by the caller. Given that we'd prefer any unwanted arguments to raise an error, we could do this (nb: add an explicit assertion that `ignore` is empty)
> As a convenient shortcut, we can simply omit the 'ignore' name, meaning 'don't allow any positional arguments beyond this point'.