5 ms·
in python i started writing any function that takes >2 arguments as either def foo(*,a,b,c)... or def foo(obvious_and_required_thing, *, a,b,c) ... and
by hprotagonist 6y ago
in python i started writing any function that takes >2 arguments as either
def foo(*,a,b,c)...
or
def foo(obvious_and_required_thing, *, a,b,c) ...
and noticed an immediate improvement in clarity.
- nafey 6y agoWhat does * represent here? The "self" object?
- masklinn 6y agoIt‘s a separator between positional and keywords. It’s * because the “rest” argument (*args) also makes following arguments keyword-only, so a lone star is essentially one which collects nothing.
- foota 6y agoSo this requires the following args to be named? Neat!
- masklinn 6y agoYes. 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'.
- sdoering 6y agoThank you very much. I am using python for some time now. Stumbled upon this, but never took the time to read up on it. If anybody also wants to know more I found this extensive post explaining the concepts behind * and in python (not in multiplication): https://treyhunner.com/2018/10/asterisks-in-python-what-they-are-and-how-to-use-them/ https://treyhunner.com/2018/10/asterisks-in-python-what-they...
- throwaway43234 6y agoI understand that this is standard practice by now, but this is one of those cases where I can't help but feel the language design team strayed too far from the original spirit of python. Now there's yet another possible interpretation of an asterisk: multiplication; splatting; exponentiation; declaring variadic arguments; and now declaring that all subsequently declared arguments are to be keyword-only. And I understand that it sort-of relates to declaring variadic arguments, but it certainly isn't declaring variadic arguments, and in my opinion reusing the same token in the same syntactic construct for two wildly different purposes is destined to cause confusion. For evidence, see the other replies: people don't know about this and can't easily tell what it does. This runs counter to the Zen: "Readability counts", "Explicit is better than implicit", "Special cases aren't special enough to break the rules.", etc. 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'}]
- kabacha 6y agoI'll disagree with you here. In Python `*` is already a extremely popular symbol for expanding and there's no usage conflict between expand and numeric multiplication. This change gels really nicely with the rest of expand ecosystem. I think it's much more beautiful and convenient than your JS example which in all honesty looks necessary cluttered and ugly.
- jakear 6y agoBut there’s no expanding happening here? So why take a token that’s immediately recognizable as either “expanding” or “muliplying-ish” and extend it to mean “or enforcing named parameters and not expanding”? > 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.
- cheez 6y agome likey