Syntactic sugar to encourage use of named arguments

In yet another syntax suggestion inspired by @vovavili,

super().__init__(
    option_strings=_option_strings,
        pass dest,
        nargs=0,
        pass default,
        pass type,
        pass choices,
        pass required,
        pass help,
        pass metavar)

reads quite natural to me, though OTOH it looks more like a statement than an expression.

4 Likes

I don’t think this is a great argument, because people read a lot of code where they have no control over what features are used. I know I do! So essentially I need to know the entire language. It’s like C+ , where everyone uses 80% of the language, because they think those are the reasonable parts. But everyone uses a different 80%.

17 Likes

13 posts were split to a new topic: My thoughts on language design

This is mostly OT but I can’t make myself ignore it.

There is a reason that I never customize linter configs more than is strictly necessary, and it’s informed by years of working in disparate codebases. The more you twiddle linting settings, the more you are defining your own style, vs the linter’s baseline state.
When you move between dozens of distinct projects on a regular basis, some managed by different teams or volunteers, this means you are reading and trying to understand code written in different styles – sometimes very different.

Stylistic differences compound with design differences and other facets of code authorship. It makes moving between projects in this way that much more difficult. This is part of why black’s value add was so quickly apparent to the community: “almost no configuration or settings? great!”

Not configuring things is a choice, and I won’t pressure anyone on this forum to make the same choice. But it should be recognized as a rational choice driven by a desire for various codebases to remain the same.

4 Likes

I am happy to see this brought up again and with such strong support compared to when I tried this a few years ago. I don’t care about the syntax, but I can say that I implemented the foo(=x) syntax in CPython in maybe an hour or two even though my C is enormously rusty and I had never looked at the CPython code!

This change would be extremely useful for me on a daily basis and to me would be the biggest improvement to Python since f-strings.

6 Likes

I’m in favor of this feature. Especially with the elegant pass param syntax suggested by @orise. Reminds me of why I really like inherit in Nix.

That being said, if choosing between param= and =param, I very strongly vote for =param due to readability. I think param= could be become hard to spot with long param names, which is ironically the one time you would really want this feature.

This syntax should immediately stand out when skimming code due to the implications of forgetting to change an invocation using implicit named args when the function signature changes.

3 Likes

If this syntactic sugar were to be accepted, which form would you prefer to see:

  • f(x=)
  • f(=x)
  • f(%x)
  • f(*, x)
  • f(pass x)
0 voters
7 Likes

I found @ntessore’s example above helpful for comparing the =x vs x=, so here they are mapped into all 5 options (and the status quo), with one positional and one explicit kwarg and 3 implicit kwargs:

Status quo

cbar = fig.colorbar(
    img,
    ax=my_axis,
    location=location,
    fraction=fraction,
    pad=pad,
)

f(x=)

cbar = fig.colorbar(
    img,
    ax=my_axis,
    location=,
    fraction=,
    pad=,
)

f(=x)

cbar = fig.colorbar(
    img,
    ax=my_axis,
    =location,
    =fraction,
    =pad,
)

f(%x)

cbar = fig.colorbar(
    img,
    ax=my_axis,
    %location,
    %fraction,
    %pad,
)

f(*, x)

cbar = fig.colorbar(
    img,
    ax=my_axis,
    *,
    location,
    fraction,
    pad,
)

f(pass x)

cbar = fig.colorbar(
    img,
    ax=my_axis,
    pass location,
    pass fraction,
    pass pad,
)

Edit: I am assuming here that the prevalent style, if not the required syntax, would be for implicit kwargs to come after any explicit kwargs.

7 Likes

I was doing the opposite actually in my experimenting, it looks better to me for the initial character syntax =arg and %arg.

cbar = fig.colorbar(                |    cbar = fig.colorbar(
    img,                            |        img,
    ax=my_axis,                     |        =location,
    =location,                      |        =fraction,
    =fraction,                      |        =pad,
    =pad,                           |        ax=my_axis,
)                                   |    )

I think you should add an example with the status quo for completeness sake.

1 Like

I like the location= syntax because I’m used to it from f strings. My mind says “ok, we’re filling the parameter named location, oh, nothing follows the equals sign, it must be an implicit keyword argument so it’s being filled with the variable of the same name.I do admit it does sometimes look like a typo.

But the =location format also looks like a typo and I don’t have a clean mental flow of what’s happening. It’s like
 ok some parameter is being filled with something, but what parameter? Oh, that parameter named location is being filled with the variable of the same name.

It seems like there are strong opinions for and against each of these options. My opinion isn’t that strong.

One question I have is how coding tools will handle refactoring by changing the name of either a variable or a keyword argument.

If I have location= passed into some function and then I rename the variable to “place” I guess I would expect the tool to refactor the function call to location=place. I guess the reverse should be true for replacing the keyword parameter name: place=location. I don’t think tools should act to maintain the implicit keyword argument, I.e. rename the variable when a keyword argument is changed.

Also, because of this refactoring problem, I think implicit keyword arguments need to be able to be used anywhere regular keyword arguments are. That is, there can’t be e.g. a language requirement that all implicit keyword args precede or follow all explicit keyword args (a convention could be ok).

1 Like

Your remarks on typos also just reminded me that students quite regularly point out “typos” in expressions such as new_list = (first, *rest). So I guess it’s all relative to us not being used to any new syntax.

1 Like

Nailed it :slight_smile: EVERY piece of new syntax looks “weird” at first. (Or sounds weird. I remember a story someone told that began with “Back in the days when the word ‘soft-ware’ sounded funny
”) That said, though, an intuition for “that looks like a typo” is sometimes a hint that something’s inconsistent with the rest of the language. It’s worth delving into WHY it looks like a typo.

Personally, I’m in favour of spam= and would also be fine with =spam, since both of them are abbreviated forms of the existing syntax (and thus easily explainable as “it’s the exact same thing on the other side of the equals sign”).

7 Likes

Thank you! Appreciate it!

I hope that this isn’t required syntax, at least for the three variants where that’s possible. It should be perfectly reasonable to mix-and-match spam=ham with the new syntax without reordering the arguments. Obviously in the case of *, spam this wouldn’t be the case, but for the other three, it shouldn’t be a problem to switch out one argument without affecting the others.

For the sake of consistency, though, what you’ve done looks great.

4 Likes

:+1: not requiring reordering is better.

To repeat a specific point from one of my longer comments above:
Two arguments may be naturally related, so that they go side-by-side, and the new syntax should not force me to split them up.

e.g.,

foo(
    bar=get_bar(),
    hide_bars=,
    baz=get_baz(),
    center_baz=,
)
5 Likes

Thanks for this. Reading these examples I changed my mind from weakly preferring =x to now moderately preferring x=. I think it hurts readability when the = comes before in this example, and this is exactly how I’d format code in my code.

I still like the symmetry of the * syntax with the existing def syntax, but I think it’s objectively worse since it requires reordering of parameters. This is bad for source code history, and potentially readability if you have some reasonable order to the arguments.

3 Likes

I was about to tell the same

Aren’t the special characters “/” and “*” in a function definition doing what you want?
See:

Close; this is the flip side of that, where the function call happens. So there’s a bit of a parallel.

2 Likes

What is the next step here? Writing up the PEP?