Positional-only arguments in Python

August 21, 2019

Positional-only arguments in Python

The ability to specify positional-only arguments using the / marker in function definitions is among the many new improvements to the Python language coming in the upcoming }}">3.8 release. This addition to syntax has performance benefits and enables better API design. Let's look at the motivation behind positional-only arguments and how to use it, with examples.

Background

Keyword-only arguments have been available in Python with the * marker, and addition of / marker for positional-only arguments improves the language’s consistency. With positional-or-keyword parameters, the mix of calling conventions is not always desirable. Consider these examples:

  • Some function parameters already have semantic meaning: namedtuple(typenames, field_names, …)
  • The parameter name has no true external meaning: arg1, arg2, …, etc for min()

If the users start using a keyword argument, the library author cannot rename the parameter because it would be a breaking change. In case of min(), the name of the parameter provides no intrinsic value and forces the author to maintain its name forever since callers might pass arguments as a keywords. This problem is solved by positional-only arguments. In addition, the parsing and handling of positional-only arguments is faster.

How to use positional-only arguments

To specify arguments as positional-only, a / marker should be added after all those arguments in the function definition. Take this example:

{{< highlight python "hl_lines=1">}} def pow(x, y, /, mod=None): r = x ** y if mod is not None: r %= mod return r {{< /highlight >}}

The following would apply:

  • All parameters to the left of / (in this case, x and y) can only be passed positionally.
  • mod can be passed positionally or with a keyword.
>>> pow(2, 10)  # valid
>>> pow(2, 10, 17)  # valid
>>> pow(2, 10, mod=17)  # valid
>>> pow(x=2, y=10)  # invalid, will raise a TypeError
>>> pow(2, y=10)  # invalid, will raise a TypeError

Another example:

def table_format(items, separator=',', /, prettify=False):
    pass
  • Once a positional-only parameter is specified with a default, the following positional-only and positional-or-keyword parameters need to have defaults as well (in this case, prettify).
  • Positional-only parameters which do not have default values are required positional-only parameters (in this case, items).

The generic case

A function definition, with all flavors of argument-passing variants, would look like this:

def f(pos1, pos2, /, pos_or_kwd, *, kwd1, kwd2):
      -----------    ----------     ----------
        |             |                  |
        |        Positional or keyword   |
        |                                - Keyword only
         -- Positional only

When to use positional-only arguments

  • Use positional-only if names do not matter or have no meaning, and there are only a few arguments which will always be passed in the same order.
  • Use keyword-only when names have meaning and the function definition is more understandable by being explicit with names.

Positional-only arguments were proposed in PEP 570, and it's worth taking a look at the doc to understand the feature in more detail.

Get started with DeepSource

DeepSource is free forever for small teams and open-source projects. Start analyzing your code in less than 2 minutes.

Choose an account
Newsletter

Read product updates, company announcements, how we build DeepSource, what we think about good code, and more.