Python Positional-Only Parameters Explained

Positional-only parameters, introduced in Python 3.8 via PEP 570, allow developers to specify function arguments that must be supplied by position rather than as keyword arguments. In a function signature, the forward slash (/) acts as a delimiter indicating that all parameters to its left cannot be passed using name=value syntax. This article explores why the slash syntax exists, its primary design advantages, and how it improves API design and code stability.

The Syntax of the Slash (/)

In Python, the forward slash divides parameters into positional-only and standard parameters. When defining a function:

def example(a, b, /, c, d, *, e, f):
    pass

Calling example(a=1, b=2, c=3, d=4, e=5, f=6) will raise a TypeError because a and b cannot be supplied as keyword arguments.

1. API Stability and Name Decoupling

The primary significance of positional-only parameters is decoupling an API's implementation from its public contract. When arguments can be passed by keyword, parameter names become part of the public interface. If an author renames a parameter in a later release, any caller relying on the old keyword name will experience broken code.

By marking parameters with /, authors reserve the right to rename parameters in future refactors without breaking backwards compatibility:

# The parameter name 'x' can be renamed to 'value' later without breaking callers
def square(x, /):
    return x ** 2

# Valid call
square(5)

# Raises TypeError: square() got some positional-only arguments passed as keyword arguments: 'x'
square(x=5)

2. Preventing Collisions with **kwargs

When a function accepts arbitrary keyword arguments via **kwargs, named parameters can create ambiguous namespace conflicts. Positional-only parameters eliminate this problem.

Consider a function that formats data using arbitrary keyword pairs:

def format_entry(template, /, **kwargs):
    return template.format(**kwargs)

Because template is positional-only, a caller can safely pass a keyword argument named template inside kwargs without causing a collision:

# This works seamlessly
format_entry("Template name: {template}", template="BaseTheme")

Without the /, Python would raise a TypeError: format_entry() got multiple values for keyword argument 'template'.

3. Emulating Built-In C Functions

Historically, many built-in functions implemented in C (such as len(), abs(), and min()) only accepted positional arguments. Before Python 3.8, pure Python functions could not replicate this behavior natively, leading to inconsistencies between built-in APIs and user-defined functions. The / syntax standardizes function signature capabilities across both pure Python and C-extension modules.

4. Enforcing Natural and Readable Call Semantics

Some arguments have obvious positional meaning, and forcing or allowing them to be passed as keywords adds noise or confusion rather than clarity. For example, mathematical coordinates or transformations:

def calculate_distance(x1, y1, x2, y2, /):
    ...

Calling calculate_distance(0, 0, 10, 10) is idiomatic and clean, whereas allowing calls like calculate_distance(x2=10, y1=0, x1=0, y2=10) degrades readability without providing any practical benefit.