Back to Blog
Python

Python Keyword-Only Star Syntax Explained

python keyword only star syntax: Learn how the bare * in Python function signatures enforces keyword-only arguments, with examples and common pitfalls.

keyword-only argumentsfunction signaturespython syntaxparameter handlingpython functions
A clean illustration of a Python function signature with a bare asterisk separating positional and keyword-only parameters.

The python keyword only star syntax refers to the bare * used in a function definition to mark all following parameters as keyword-only. This means callers must pass those arguments by name, not by position. It is a small syntax feature that has a significant impact on API design and code readability.

What the Bare * Does in a Function Signature

In Python, the * appears in a function signature in two distinct ways. The first is as *args, which collects extra positional arguments into a tuple. The second is as a bare *, which does not collect anything. Instead, it acts as a separator: every parameter declared after it becomes keyword-only.

Consider this function:

def configure(host, port, *, timeout, retries): pass

Here, host and port are positional-or-keyword parameters, meaning callers can pass them either by position or by name. timeout and retries are keyword-only. The bare * tells Python that no further positional arguments are allowed after that point.

This behavior is enforced at call time. If you try to call configure("localhost", 8080, 5, 3), Python raises a TypeError because the function takes exactly 2 positional arguments but 4 were given. The only way to pass timeout and retries is by using their names.

Minimal Example: Enforcing Keyword-Only Parameters

The most common use of the bare * is to force callers to be explicit about certain arguments. This is especially useful when a parameter's meaning is not obvious from its position alone.

def send_message(recipient, *, subject=None, priority=1): print(f"To: {recipient}") if subject: print(f"Subject: {subject}") print(f"Priority: {priority}")

Calling send_message("alice@example.com", "Hello", 2) fails because subject and priority are keyword-only. The correct call is:

send_message("alice@example.com", subject="Hello", priority=2)

This prevents a caller from accidentally swapping subject and priority when both are integers or strings. It also makes the call site self-documenting.

Mixing Positional, Keyword-Only, and Variable-Length Parameters

The bare * can appear alongside *args and / (positional-only marker). The general order is:

  1. Positional-only parameters (before /)
  2. Positional-or-keyword parameters (between / and * or *args)
  3. *args or bare *
  4. Keyword-only parameters
  5. **kwargs

Here is an example that combines all of them:

def process(data, /, mode, *, verbose=False, **options): pass

data is positional-only, mode is positional-or-keyword, and verbose is keyword-only. **options collects any additional keyword arguments. This layout gives you fine-grained control over how callers interact with your function.

When you use *args instead of a bare *, the parameters after *args are also keyword-only. The difference is that *args also captures extra positional arguments. If you do not need to capture extra positional arguments, use the bare * to avoid unnecessary tuple creation and to signal that the function has a fixed number of positional parameters.

Common Mistakes and Misconceptions

One frequent mistake is forgetting the comma after the bare *. In Python, * is a valid expression, so def f(*, a): is correct, but def f(* a): is a syntax error. The comma is required to separate the * from the first keyword-only parameter.

Another misconception is that the bare * itself can be used as a parameter name. It cannot. It is purely a marker. You cannot access the value of * inside the function body.

Some developers confuse the bare * with the unpacking operator used in function calls. In a call, *iterable unpacks an iterable into positional arguments. In a definition, * marks the boundary for keyword-only parameters. These are distinct contexts and should not be mixed.

A third mistake is placing the bare * after **kwargs. That is invalid because **kwargs must be the last parameter. The correct order always places **kwargs at the end.

When to Use Keyword-Only Arguments

Keyword-only arguments are most valuable when a function has several parameters that are optional or have similar types. For example, a function that creates a user might have name, email, age, and location. Requiring age and location to be keyword-only prevents calls like create_user("Alice", "alice@example.com", 30, "NYC") where the meaning of 30 and "NYC" is ambiguous.

They are also useful in APIs that evolve. If you add a new parameter to a function, making it keyword-only avoids breaking existing positional calls. Existing callers that used positional arguments will continue to work, and new callers must use the keyword form, which is clearer.

However, keyword-only arguments are not always appropriate. If a function has a natural positional order and every parameter is required, forcing keyword-only usage adds verbosity without much benefit. Use the bare * when clarity or safety outweighs the extra typing.

Compatibility and Maintainability Considerations

The bare * was introduced in Python 3.0, so any codebase running Python 3 can use it without compatibility issues. If you maintain a library that supports Python 2, you cannot use this syntax, but Python 2 reached end-of-life in 2020, so this is rarely a concern today.

From a maintainability perspective, keyword-only arguments make call sites more readable and reduce the chance of errors when parameters are reordered. They also make it easier to add new parameters later without breaking existing callers, as long as the new parameters are placed after the bare *.

One tradeoff is that keyword-only arguments cannot be passed positionally, which can be inconvenient for functions that are called frequently with many arguments. In such cases, the extra verbosity at the call site may outweigh the safety benefit. Consider the audience of your function and how it will be used before deciding.

Runtime Behavior and Performance Notes

Enforcing keyword-only arguments is a compile-time and call-time check. There is no measurable runtime overhead compared to regular parameters. The TypeError for passing too many positional arguments is raised quickly, before the function body executes.

Using *args instead of a bare * does have a small runtime cost because Python must create a tuple for the collected positional arguments. If you do not need that tuple, using the bare * avoids the allocation. In performance-sensitive code, this difference is negligible unless the function is called millions of times, but it is still a reason to prefer the bare * when you do not need variable-length positional arguments.

The bare * also interacts with introspection tools. Tools like inspect.signature correctly report keyword-only parameters. This makes it easier for documentation generators and IDEs to show accurate signatures.

Advanced Usage: Forcing Keyword-Only in Class Constructors

The bare * is especially useful in __init__ methods where you want to prevent callers from relying on positional argument order. For example:

class DatabaseConnection: def __init__(self, host, port, *, user, password): self.host = host self.port = port self.user = user self.password = password

This forces user and password to be passed by name, which is safer because they are sensitive and easy to confuse. It also makes the constructor call more explicit:

conn = DatabaseConnection("localhost", 5432, user="admin", password="secret")

If you later decide to add an optional ssl parameter, you can add it after the * without breaking existing calls that used positional arguments for user and password.

Parameter Kind Summary

Parameter kindMarkerCan be passed by position?Can be passed by keyword?
Positional-only/YesNo
Positional-or-keyword(none)YesYes
Keyword-only*NoYes
Var-positional*argsYes (as extra args)No
Var-keyword**kwargsNoYes (as extra kwargs)

This table summarizes the five parameter kinds. The bare * is the marker for keyword-only parameters. It is a deliberate design choice that improves API clarity and reduces the chance of misuse.

python keyword only star syntax: Practical Usage and Code Ex | RYUSLOG DEV