Positional-Only and Keyword-Only Arguments

25 Sep 2018

One of the nice Python 3 features is keyword-only arguments for functions. Keyword-only arguments are usually used to represent truly optional function arguments.

Let’s have a look of its syntax:

def f(a, b, *args, option=True):

Or, if you don’t want to collect *args, you can do the following:

def f(a, b, *, option=True):

option is the keyword-only argument. The only way to access it is to explicitly call f(a, b, option=True).

One of the problems it is solving is that the values of keyword arguments can be passed without specifying the keyword(s); or, put it in another way, keyword arguments are not keyword-only. And in this case it is possible to actually pass a value as a positional argument to a keyword argument accidentally: example.

The other benefit of using keyword-only arguments is you can make youre APIs ‘future change proof’. To put it in another way: using keyword-only arguments allows you to add new keyword (-only) arguments without breaking API. Let’s see this example.

Without this feature, like what’s in Python 2, you have to use **kwargs and do the extra handling/parsing yourself.

Enough said, a little summary:

If you somehow are writing for a Python 3 only codebase, I highly recommend making all your keyword arguments keyword only, especially keyword arguments that represent “options”

Now, let’s talk about positional-only arguments. Say what?

Positional-only arguments are parameters that cannot be specified by keywords, and they are passed only by mapping the positions to the formal arguments of a function. Many python builtin functions are actually allowing only positoinal-only arguments, for example, function pow:

In [1]: pow?
Signature: pow(x, y, z=None, /)
Equivalent to x**y (with two arguments) or x**y % z (with three arguments)

Some types, such as ints, are able to use a more efficient algorithm when
invoked using the three argument form.
Type:      builtin_function_or_method

In [3]: pow(x=2, y=3)
TypeError                                 Traceback (most recent call last)
<ipython-input-3-3c1748947c84> in <module>()
----> 1 pow(x=2, y=3)

TypeError: pow() takes no keyword arguments

Noticed the slash / at the end of the argument list? That means all arguments before it are positional-only. You can sometimes see such syntax in some of the docstrings of certain builtin functions or library functions.

Currently (25.09.2018) there is no way to write ‘positional-only’ arguments in Python 3, to use such feature, you need to use Python C API (in other words, implement functions in C). PEP 570 was already proposed for implementing such syntax in Python, however, it is not yet implementd as we speak (25.09.2018).

Given such feature in mind, the following syntax gives the complete picture of all possible types of arguments in a function signature:

def name(positional_only_parameters, /, positional_or_keyword_parameters,
         *, keyword_only_parameters):

One last note, even in the above newer syntax, non-default arguments must still appear before default arguments (for positional_only_parameters and positional_or_keyword_parameters).