Modern Python Cookbook
上QQ阅读APP看书,第一时间看更新

Defining position-only parameters with the / separator

In Python 3.8, an additional annotation was added to function definitions. We can use the / character in the parameter list to separate the parameters into two groups. Before /, all parameters work positionally, or names may not be used with argument values. After /, parameters may be given in order, or names may be used.

This should be used for functions where the following conditions are all true:

  • A few positional parameters are used (no more than three)
  • They are all required
  • The order is so obvious that any change might be confusing

This has always been a feature of the standard library. As an example, the math.sin() function can only use positional parameters. The formal definition is as follows:

>>> help(math.sin)
Help on built-in function sin in module math:
sin(x, /)
    Return the sine of x (measured in radians).

Even though there's an x parameter name, we can't use this name. If we try to, we'll see the following exception:

>>> import math
>>> math.sin(x=0.5)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: sin() takes no keyword arguments

The x parameter can only be provided positionally. The output from the help() function provides a suggestion of how the / separator can be used to make this happen.

Getting ready

Position-only parameters are used by some of the internal built-ins; the design pattern can also be helpful, though, in our functions. To be useful, there must be very few position-only parameters. Since most mathematical operators have one or two operands, this suggests one or two position-only parameters can be useful.

We'll consider two functions for conversion of units from the Fahrenheit system used in the US and the Centigrade system used almost everywhere else in the world:

  • Convert from F into C: C = 5(F-32) / 9
  • Convert from C into F: F = 32 + C(9/5)

Each of these functions has a single argument, making it a reasonable example for a position-only parameter.

How to do it…

  1. Define the function:
    def F(c: float) -> float:
        return 32 + c*(9/5)
    
  2. Add the / parameter separator after the position-only parameters:
    def F(c: float, /) -> float:
        return 32 + c*(9/5)
    

How it works…

The / separator pides the parameter names into two groups. In front of / are parameters where the argument values must be provided positionally: named argument values cannot be used. After / are parameters where names are permitted.

Let's look at a slightly more complex version of our temperature conversions:

def C(f: float, /, truncate: bool=False) -> float:
    c = 5*(f-32) / 9
    if truncate:
        return round(c, 0)
    return c

This function has a position-only parameter named f. It also has the truncate parameter, which can be provided by name. This leads to three separate ways to use this function, as shown in the following examples:

>>> C(72)
22.22222222222222
>>> C(72, truncate=True)
22.0
>>> C(72, True)
22.0

The first example shows the position-only parameter and the output without any rounding. This is an awkwardly complex-looking value.

The second example uses the named parameter style to set the non-positional parameter, truncate, to True. The third example provides both argument values positionally.

There's more…

This can be combined with the * separator to create very sophisticated function signatures. The parameters can be decomposed into three groups:

  • Parameters before the / separator must be given by position. These must be first.
  • Parameters after the / separator can be given by position or name.
  • Parameters after the * separator must be given by name only. These names are provided last, since they can never be matched by position.

See also

  • See the Forcing keyword-only arguments with the * separator recipe for details on the * separator.