Positional-Only and Keyword-Only Parameters in Python

Learn how to enforce calling conventions with the / and * syntax in Python function signatures, making parameters positional-only or keyword-only.

7 min read

The articles on positional and keyword arguments in Python and default parameter values covered how callers can choose between positional and keyword styles and how defaults make parameters optional. Now you need the syntax that lets you, as the function author, enforce which calling style the caller must use for each parameter. Python provides two markers for this purpose: the slash (/) for positional-only parameters and the asterisk (*) for keyword-only parameters. Together, they let you design function signatures that are precise about how each parameter should be passed, making your interfaces both safer and more evolvable.

Before Python 3.8 added the slash syntax, function authors had no way to prevent callers from passing parameters by keyword. This meant that the parameter names you chose became permanent parts of your function's public API. Renaming a parameter, even one that was never intended to be used as a keyword by callers, could break code that happened to pass it by name. The slash solved this by giving authors an explicit way to say "these parameters are positional-only, and their names are not part of the contract." Similarly, the asterisk for keyword-only parameters has existed since Python 3.0, and it gives authors a way to say "these parameters must be named, because their meaning is not clear from position alone."

Positional-only parameters with the slash

The slash (/) appears in the parameter list as a standalone marker. Every parameter before the slash is positional-only and cannot be passed as a keyword argument. Attempting to do so raises a TypeError with a message that clearly explains the restriction:

pythonpython
def multiply(a, b, /):
    return a * b
 
result = multiply(3, 4)          # Valid
result = multiply(a=3, b=4)      # TypeError!

The parameters a and b are positional-only because they appear before the slash. The first call works because both arguments are passed by position. The second call fails because keyword arguments are forbidden for positional-only parameters, even when the keyword name exactly matches the parameter name. This enforcement is absolute, and it applies regardless of whether the caller's intent was correct.

Positional-only parameters are ideal when the parameter names are implementation details that might change in future versions of your code. If you write a function that processes data and you name the input parameter raw_input, but you later decide that data or source is a better name, you can rename it freely as long as it stays positional-only. Callers who used the function with positional arguments are unaffected by the rename because their code never mentioned the parameter name. If the parameter had been usable as a keyword argument, renaming it would break every call site that used the old name. Many of Python's own built-in functions use positional-only parameters for exactly this reason. The len function accepts a single positional-only argument, which is why you write len(my_list) and never len(obj=my_list).

Keyword-only parameters with the asterisk

A bare asterisk (*) in the parameter list, not followed by a parameter name and not part of *args, acts as a separator. All parameters after the asterisk are keyword-only and must be passed by name. Attempting to pass them positionally raises a TypeError:

pythonpython
def connect(host, *, port=5432, timeout=30):
    print(f"Connecting to {host}:{port}, timeout={timeout}")
 
connect("db.example.com", port=3306, timeout=10)   # Valid
connect("db.example.com", 3306, 10)                 # TypeError!

The parameters port and timeout are keyword-only because they appear after the asterisk. The first call works because both optional parameters are passed by name. The second call fails because positional arguments after the first one cannot be matched to keyword-only parameters, even though the values and positions are correct. This might seem restrictive, but it protects against a real class of bugs where the meaning of a positional value is ambiguous.

Consider a function with the signature def search(query, case_sensitive=False, max_results=10). A call like search("python", True, 5) is ambiguous: does True mean case-sensitive search or something else? Does 5 mean five results or five seconds of timeout? The reader has to look up the function definition to understand each positional value. With keyword-only parameters, the same call becomes search("python", case_sensitive=True, max_results=5), which is self-documenting. The labels make the intent explicit, and the reader does not need to memorize or look up the parameter order.

Combining the slash and asterisk in one signature

Python allows both markers in a single function signature, and the order is fixed: positional-only parameters first, then the slash, then parameters that accept either style, then the asterisk, then keyword-only parameters. This ordering supports any combination of calling conventions:

pythonpython
def register(user_id, /, name, *, role="user", active=True):
    print(f"ID={user_id}, Name={name}, Role={role}, Active={active}")

The parameter user_id is positional-only because it appears before the slash. The parameter name accepts either positional or keyword arguments because it sits between the slash and the asterisk. The parameters role and active are keyword-only because they appear after the asterisk. This layered design reflects the intent of each parameter: user_id is an internal identifier whose name should not matter to callers, name is a natural second positional argument but could also be passed by keyword for clarity, and role and active are configuration flags whose meaning should always be explicit through labels.

The practical benefit of this layered approach is that you can evolve the function's signature over time with minimal risk of breaking existing callers. You might add new keyword-only parameters after the asterisk, and old callers are unaffected because they never passed those parameters and the new ones have defaults. You might rename the positional-only parameter before the slash, and callers who used positional arguments are unaffected because they never referenced the old name. The flexible parameters in the middle remain stable because they represent the core inputs that are unlikely to change.

When positional-only is the right choice

Use positional-only parameters when the parameter name would not add clarity for the caller, when the name is an implementation detail subject to change, or when the function's semantics depend on position rather than name. Mathematical functions often fall into this category: a function that calculates distance between two points naturally takes two positional arguments, and naming them point_a and point_b adds no value over the caller simply providing the two values in order.

Positional-only is also appropriate when you want to reserve the right to add keyword parameters with the same names in the future. If a parameter is currently named mode but you anticipate adding a more specific mode parameter later, making the current mode positional-only prevents callers from using it as a keyword, which frees the name for future use. This is a forward-compatibility strategy that library authors use to avoid painting themselves into a corner with parameter names.

When keyword-only is the right choice

Use keyword-only parameters for any value whose meaning is not obvious from its position in a function call. Boolean flags are the most common example: True or False in the middle of a call communicates nothing about what that boolean controls. Making the flag keyword-only forces the caller to write the parameter name, which turns an opaque True into an explicit case_sensitive=True. Any parameter that accepts a "magic number" whose significance is unclear, like a timeout in seconds, a retry count, or a buffer size, also benefits from being keyword-only.

Use keyword-only when a function has many optional parameters and most callers will only need to override a few of them. Forcing all optional parameters to be keyword-only prevents the situation where a caller must pass placeholder values for parameters they do not want to change just to reach the one parameter they do want to set. A function with ten optional parameters, all keyword-only, lets the caller specify only the two that matter and let the rest default.

Designing function signatures for readability

A well-designed function signature tells the caller how to use the function without requiring them to read documentation or implementation code. The parameter order, the presence of defaults, and the choice of positional-only or keyword-only markers all contribute to this readability. Place the most important and most frequently passed parameters first, whether they are positional-only or flexible. Place parameters with sensible defaults after required parameters. Reserve the end of the signature for keyword-only configuration flags that most callers will leave at their defaults.

The built-in sorted function is a good model: it accepts an iterable as the first positional argument, then has keyword-only parameters for key and reverse that most callers leave at their defaults. A beginner can write sorted(my_list) and get correct results. An experienced user can write sorted(my_list, key=len, reverse=True) and get fine-grained control. The signature supports both use cases without either one compromising the other. This article's patterns, combined with the default value techniques from default parameter values in Python functions, give you the full toolkit for designing signatures that follow this model.

Rune AI

Rune AI

Key Insights

  • The / symbol makes all parameters before it positional-only; they cannot be passed as keyword arguments.
  • The * symbol makes all parameters after it keyword-only; they must be passed by name.
  • Use positional-only parameters when the parameter names are internal details that may change in future versions.
  • Use keyword-only parameters for boolean flags and configuration values whose meaning is unclear from position.
  • The full parameter order is: positional-only, slash, flexible, asterisk, keyword-only.
RunePowered by Rune AI

Frequently Asked Questions

What does the forward slash (/) mean in a Python function definition?

The slash indicates that all parameters before it are positional-only. They cannot be passed as keyword arguments. This syntax was added in Python 3.8 and is used in many built-in functions where the parameter names are implementation details that should not become part of the public API.

What does a bare asterisk (*) mean in a Python function definition?

A bare asterisk, not followed by a parameter name, marks the boundary between positional and keyword-only parameters. All parameters after the asterisk must be passed as keyword arguments. The asterisk itself consumes no arguments; it acts purely as a separator.

Can I use both / and * in the same Python function signature?

Yes. The order must be: positional-only parameters, then a slash, then regular parameters that accept both styles, then an asterisk, then keyword-only parameters. This lets you specify the calling convention for each parameter: some positional-only, some flexible, some keyword-only.

Conclusion

The slash and asterisk syntax gives you precise control over how callers pass arguments to your functions. Positional-only parameters protect implementation details from becoming part of the public API. Keyword-only parameters make function calls self-documenting for values whose meaning is not obvious from position alone. Using these tools deliberately results in function interfaces that are both harder to misuse and easier to evolve.