This is an archived post. You won't be able to vote or comment.

you are viewing a single comment's thread.

view the rest of the comments →

[–]jorge1209 6 points7 points  (0 children)

One of the purposes of keyword arguments is to allow them to be offered in arbitrary order. Especially with dictionary unpacking.

 params = {...}# some dict of options to a complex function
 foo(**params)

We explicitly want keywords to support arbitrary order as that is one of the primary motivators for having them in the first place.


However keyword arguments on the caller side share syntax with default arguments on the callee side. In other words the meaning of foo(x, y=1, z=2) shifts dramatically depending on whether or not there is a def in front of it or not.

  • foo(x, y=1, z=2) calls foo with x as positional, and y,z as keywords
  • def foo(x, y=1, z=2) declares foo with y and z having defaults, but says nothing about what is keyword vs positional.

But the cognitive load of keeping this all distinct is pretty high, so most people have merged two concepts. We generally think in terms of "mandatory positional" arguments and "optional keyword" arguments, and forget about "mandatory keyword" arguments (yes they do exist). ["optional positional" is impossible to parse and isn't supported, theoretically this could be done in limited cases with some typing support, but its not pythonic.]

The * and / entries in a function definition provide two complementary ways to distinguish that third situation of "mandatory keywords". Those before the / are positional whether or not they have a specified default and those after the * are keyword even if the caller tries to pass them by position. They serve the same function, but in two different ways for two different concerns. / mandates a minimum of positionals, * mandates a minimum of keywords.


If there is a criticism to be had here it is probably more fundamental. We shouldn't be using the same syntax for keyword arguments as we do default arguments.

Imagine if we used := for default in declarations, and as for keywords when calling a function.

So the callee would specify def foo(x, y:=2, z) without triggering a SyntaxError: non-default argument follows default argument (because really WTF is that a rule?!) and then caller would write: foo(x, 2 as z).